Linux Power Management development
 help / color / mirror / Atom feed
* [RFC PATCH 0/2] cpufreq/amd-pstate: Fixes for "Requested CPU Min frequency" support
@ 2026-07-15  7:48 K Prateek Nayak
  2026-07-15  7:48 ` [RFC PATCH 1/2] cpufreq/amd-pstate: Set min_limit_freq based on bios_min_perf K Prateek Nayak
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: K Prateek Nayak @ 2026-07-15  7:48 UTC (permalink / raw)
  To: Huang Rui, Mario Limonciello, Rafael J. Wysocki, Viresh Kumar
  Cc: Mario Limonciello (AMD), Perry Yuan, K Prateek Nayak, linux-pm,
	linux-kernel

It was noted during internal testing that amd-pstate active mode with
performance governor always sets the min_perf to nominal_perf despite
BIOS having supplied bios_min_perf as the preferred idling perf.

Users who set the "Requested CPU Min frequency" from BIOS are known to
have profiled their workload at different operating frequency to know
the best configuration and amd-pstate should use the same as the lower
limit when configured.

While testing the fix for above, it was noted that kexec fails to
persist bios_min_freq even when both, the old and new kernel are aware
of bios_min_perf.

The suspend, offlining paths switched to persisting the CPPC_REQ MSR
state at the time of suspend / offlining with only min_perf being reset
to bios_min_perf.

msr_init() path only accepts the configured min_perf as bios_min_perf
when it finds rest of the bits in CPPC_REQ MSR to be 0. This is
intentional to prevent stale value of last CPPC_REQ from kernels that
are not aware of bios_min_perf to be mistakenly interpreted as the
bios_min_perf during kexec boot.

Work around this limitation by stashing the state of CPPC_REQ during
suspend / offlining in cpudata and then only programming the
bios_min_perf into CPPC_REQ while keeping the rest of the bits as 0.

Resume / onlining callbacks use the stashed state to restore the
CPPC_REQ before the CPU resumes normal operation.

Patches are prepared on top of linux-pm:bleeding-edge at commit
0e4eef86f0f4d ("Merge branch 'thermal-intel' into bleeding-edge").

-- 
K Prateek Nayak (2):
  cpufreq/amd-pstate: Set min_limit_freq based on bios_min_perf
  cpufreq/amd-pstate: Correctly reset bios_min_perf during suspend and
    offlining

 drivers/cpufreq/amd-pstate.c | 82 ++++++++++++++++++++++++++++--------
 1 file changed, 65 insertions(+), 17 deletions(-)


base-commit: 0e4eef86f0f4df58cb7526fdb968bb779fe66d81
-- 
2.34.1


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

* [RFC PATCH 1/2] cpufreq/amd-pstate: Set min_limit_freq based on bios_min_perf
  2026-07-15  7:48 [RFC PATCH 0/2] cpufreq/amd-pstate: Fixes for "Requested CPU Min frequency" support K Prateek Nayak
@ 2026-07-15  7:48 ` K Prateek Nayak
  2026-07-16 19:54   ` Mario Limonciello
  2026-07-15  7:48 ` [RFC PATCH 2/2] cpufreq/amd-pstate: Correctly reset bios_min_perf during suspend and offlining K Prateek Nayak
  2026-07-16 19:50 ` [RFC PATCH 0/2] cpufreq/amd-pstate: Fixes for "Requested CPU Min frequency" support Mario Limonciello
  2 siblings, 1 reply; 8+ messages in thread
From: K Prateek Nayak @ 2026-07-15  7:48 UTC (permalink / raw)
  To: Huang Rui, Mario Limonciello, Rafael J. Wysocki, Viresh Kumar
  Cc: Mario Limonciello (AMD), Perry Yuan, K Prateek Nayak, linux-pm,
	linux-kernel

amd_pstate_update_min_max_limit() sets the min_limit_perf to the
nominal_perf to avoid frequency throttling when the system is idling.

This was found to be an ideal default but is suboptimal for users who
have profiled their workload at different operating frequencies and have
configured the optimal idling frequency via bios_min_perf.

Use the bios_min_perf (if configured) as the min_limit_perf when running
with performance governor. In absence of bios_min_perf, continue using
nominal_perf as the default min_limit_perf to avoid throttling.

Fixes: 608a76b65288 ("cpufreq/amd-pstate: Add support for the "Requested CPU Min frequency" BIOS option")
Signed-off-by: K Prateek Nayak <kprateek.nayak@amd.com>
---
 drivers/cpufreq/amd-pstate.c | 21 +++++++++++++++------
 1 file changed, 15 insertions(+), 6 deletions(-)

diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index f8d8288d644f8..a6e43db6efefe 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -702,9 +702,12 @@ static void amd_pstate_update_min_max_limit(struct cpufreq_policy *policy)
 	WRITE_ONCE(cpudata->max_limit_freq, policy->max);
 
 	if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) {
+		u8 min_limit_perf = perf.bios_min_perf ?: perf.nominal_perf;
+		u32 min_limit_freq;
+
 		/*
-		 * For performance policy, set MinPerf to nominal_perf rather than
-		 * highest_perf or lowest_nonlinear_perf.
+		 * For performance policy, set MinPerf to nominal_perf / bios_min_perf
+		 * rather than highest_perf or lowest_nonlinear_perf.
 		 *
 		 * Per commit 0c411b39e4f4c, using highest_perf was observed
 		 * to cause frequency throttling on power-limited platforms, leading to
@@ -712,11 +715,17 @@ static void amd_pstate_update_min_max_limit(struct cpufreq_policy *policy)
 		 * performance too much for HPC workloads requiring high frequency
 		 * operation and minimal wakeup latency from idle states.
 		 *
-		 * nominal_perf therefore provides a balance by avoiding throttling
-		 * while still maintaining enough performance for HPC workloads.
+		 * nominal_perf therefore provides a balanced default by avoiding
+		 * throttling while still maintaining enough performance for HPC
+		 * workloads when bios_min_perf is not available.
+		 *
+		 * When bios_min_perf is available, users have profiled their workloads
+		 * to understand the best idling frequency. Use that instead.
 		 */
-		perf.min_limit_perf = min(perf.nominal_perf, perf.max_limit_perf);
-		WRITE_ONCE(cpudata->min_limit_freq, min(cpudata->nominal_freq, cpudata->max_limit_freq));
+		min_limit_freq = perf_to_freq(perf, cpudata->nominal_freq, min_limit_perf);
+		perf.min_limit_perf = min_limit_perf;
+
+		WRITE_ONCE(cpudata->min_limit_freq, min(min_limit_freq, cpudata->max_limit_freq));
 	} else {
 		perf.min_limit_perf = freq_to_perf(perf, cpudata->nominal_freq, policy->min);
 		WRITE_ONCE(cpudata->min_limit_freq, policy->min);
-- 
2.34.1


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

* [RFC PATCH 2/2] cpufreq/amd-pstate: Correctly reset bios_min_perf during suspend and offlining
  2026-07-15  7:48 [RFC PATCH 0/2] cpufreq/amd-pstate: Fixes for "Requested CPU Min frequency" support K Prateek Nayak
  2026-07-15  7:48 ` [RFC PATCH 1/2] cpufreq/amd-pstate: Set min_limit_freq based on bios_min_perf K Prateek Nayak
@ 2026-07-15  7:48 ` K Prateek Nayak
  2026-07-16 19:57   ` Mario Limonciello
  2026-07-16 19:50 ` [RFC PATCH 0/2] cpufreq/amd-pstate: Fixes for "Requested CPU Min frequency" support Mario Limonciello
  2 siblings, 1 reply; 8+ messages in thread
From: K Prateek Nayak @ 2026-07-15  7:48 UTC (permalink / raw)
  To: Huang Rui, Mario Limonciello, Rafael J. Wysocki, Viresh Kumar
  Cc: Mario Limonciello (AMD), Perry Yuan, K Prateek Nayak, linux-pm,
	linux-kernel

msr_init_perf() accepts the configured bios_min_perf in CPPC_REQ MSR
only if it finds the other fields (bits[63:16] + bits[7:0]) to be 0.

This is intentional: a kernel unaware of bios_min_perf can leave the
last perf request programmed in the CPPC_REQ MSR before kexec and the
newer kernel, which is aware of bios_min_perf, should avoid incorrectly
interpreting this stale value form last CPPC_REQ as the bios_min_perf.

A kernel aware of bios_min_perf is expected to reprogram the CPPC_REQ as
seen at the time of reset during suspend / onlining. This allows for
kexeced kernel to pick the bios_min_freq during boot without needing
complex mechanisms like KHO.

Although suspend / offline callbacks correctly restore min_perf back to
bios_min_perf, they fail to set other fields to 0s which causes the
kexeced kernel to think the CPPC_REQ is configured with a stale value
from the last boot.

Restore the CPPC_REQ seen at the time of the boot during suspend /
offline.

The "cpudata->cppc_req_cached" is temporarily clobbered when the CPU is
suspended / offlined to reflect the perf state at that time. A
subsequent resume / onlining will use this clobbered data to reconstruct
the CPPC request and force a reprogramming of hardware to restore the
state back.

Since cpufreq is suspended at the time of clobbering, the state will
persist, undisturbed, until the CPU resumes activity.

Fixes: 85d7dda5a9f6 ("cpufreq/amd-pstate: Fix a regression leading to EPP 0 after hibernate")
Fixes: ba3319e59057 ("cpufreq/amd-pstate: Fix a regression leading to EPP 0 after resume")
Signed-off-by: K Prateek Nayak <kprateek.nayak@amd.com>
---
 drivers/cpufreq/amd-pstate.c | 61 +++++++++++++++++++++++++++++-------
 1 file changed, 50 insertions(+), 11 deletions(-)

diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index a6e43db6efefe..6ab769d4bd2da 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -2044,6 +2044,8 @@ static int amd_pstate_cpu_online(struct cpufreq_policy *policy)
 {
 	struct amd_cpudata *cpudata = policy->driver_data;
 	union perf_cached perf = READ_ONCE(cpudata->perf);
+	u64 cppc_req_cached = cpudata->cppc_req_cached;
+	u8 min_perf, max_perf, des_perf, epp;
 	u8 cached_floor_perf;
 	int ret;
 
@@ -2051,6 +2053,22 @@ static int amd_pstate_cpu_online(struct cpufreq_policy *policy)
 	if (ret)
 		return ret;
 
+
+	max_perf = FIELD_GET(AMD_CPPC_MAX_PERF_MASK, cppc_req_cached);
+	des_perf = FIELD_GET(AMD_CPPC_DES_PERF_MASK, cppc_req_cached);
+	min_perf = FIELD_GET(AMD_CPPC_MIN_PERF_MASK, cppc_req_cached);
+	epp = FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cppc_req_cached);
+
+	/*
+	 * During offline, cppc_req_cached is clobbered with the state at the time of
+	 * hotplug. Retrieve the relevant fields and then force a mismatch for the H/W
+	 * to update with the correct values.
+	 */
+	cpudata->cppc_req_cached++;
+	ret = amd_pstate_update_perf(policy, min_perf, des_perf, max_perf, epp, false);
+	if (ret)
+		return ret;
+
 	cached_floor_perf = freq_to_perf(perf, cpudata->nominal_freq, cpudata->floor_freq);
 	return amd_pstate_set_floor_perf(policy, cached_floor_perf);
 }
@@ -2059,6 +2077,7 @@ static int amd_pstate_cpu_offline(struct cpufreq_policy *policy)
 {
 	struct amd_cpudata *cpudata = policy->driver_data;
 	union perf_cached perf = READ_ONCE(cpudata->perf);
+	u64 cppc_req_cached = cpudata->cppc_req_cached;
 	int ret;
 
 	/*
@@ -2066,14 +2085,13 @@ static int amd_pstate_cpu_offline(struct cpufreq_policy *policy)
 	 * min_perf value across kexec reboots. If this CPU is just onlined normally after this, the
 	 * limits, epp and desired perf will get reset to the cached values in cpudata struct
 	 */
-	ret = amd_pstate_update_perf(policy, perf.bios_min_perf,
-				     FIELD_GET(AMD_CPPC_DES_PERF_MASK, cpudata->cppc_req_cached),
-				     FIELD_GET(AMD_CPPC_MAX_PERF_MASK, cpudata->cppc_req_cached),
-				     FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cpudata->cppc_req_cached),
-				     false);
+	ret = amd_pstate_update_perf(policy, perf.bios_min_perf, 0U, 0U, 0U, false);
 	if (ret)
 		return ret;
 
+	/* HACK: See the comment in amd_pstate_suspend() below. */
+	cpudata->cppc_req_cached = cppc_req_cached;
+
 	return amd_pstate_set_floor_perf(policy, cpudata->bios_floor_perf);
 }
 
@@ -2081,6 +2099,7 @@ static int amd_pstate_suspend(struct cpufreq_policy *policy)
 {
 	struct amd_cpudata *cpudata = policy->driver_data;
 	union perf_cached perf = READ_ONCE(cpudata->perf);
+	u64 cppc_req_cached = cpudata->cppc_req_cached;
 	int ret;
 
 	/*
@@ -2088,14 +2107,17 @@ static int amd_pstate_suspend(struct cpufreq_policy *policy)
 	 * min_perf value across kexec reboots. If this CPU is just resumed back without kexec,
 	 * the limits, epp and desired perf will get reset to the cached values in cpudata struct
 	 */
-	ret = amd_pstate_update_perf(policy, perf.bios_min_perf,
-				     FIELD_GET(AMD_CPPC_DES_PERF_MASK, cpudata->cppc_req_cached),
-				     FIELD_GET(AMD_CPPC_MAX_PERF_MASK, cpudata->cppc_req_cached),
-				     FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cpudata->cppc_req_cached),
-				     false);
+	ret = amd_pstate_update_perf(policy, perf.bios_min_perf, 0U, 0U, 0U, false);
 	if (ret)
 		return ret;
 
+	/*
+	 * HACK: Restore the cppc_req_cached at the time of suspend. This is
+	 * used during amd_pstate_epp_resume() to restore the correct EPP value
+	 * when this CPU resumes.
+	 */
+	cpudata->cppc_req_cached = cppc_req_cached;
+
 	ret = amd_pstate_set_floor_perf(policy, cpudata->bios_floor_perf);
 	if (ret)
 		return ret;
@@ -2131,9 +2153,26 @@ static int amd_pstate_epp_resume(struct cpufreq_policy *policy)
 	u8 cached_floor_perf;
 
 	if (cpudata->suspended) {
+		u64 cppc_req_cached = cpudata->cppc_req_cached;
+		u8 min_perf, max_perf, des_perf, epp;
 		int ret;
 
-		/* enable amd pstate from suspend state*/
+		max_perf = FIELD_GET(AMD_CPPC_MAX_PERF_MASK, cppc_req_cached);
+		des_perf = FIELD_GET(AMD_CPPC_DES_PERF_MASK, cppc_req_cached);
+		min_perf = FIELD_GET(AMD_CPPC_MIN_PERF_MASK, cppc_req_cached);
+		epp = FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cppc_req_cached);
+
+		/*
+		 * Enable amd-pstate from suspend state. During suspend, cppc_req_cached
+		 * is clobbered with the state at the time of suspend. Retrieve the
+		 * relevant fields and then force a mismatch for the H/W to update with
+		 * the correct values.
+		 */
+		cpudata->cppc_req_cached++;
+		ret = amd_pstate_update_perf(policy, min_perf, des_perf, max_perf, epp, false);
+		if (ret)
+			return ret;
+
 		ret = amd_pstate_epp_update_limit(policy, false);
 		if (ret)
 			return ret;
-- 
2.34.1


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

* Re: [RFC PATCH 0/2] cpufreq/amd-pstate: Fixes for "Requested CPU Min frequency" support
  2026-07-15  7:48 [RFC PATCH 0/2] cpufreq/amd-pstate: Fixes for "Requested CPU Min frequency" support K Prateek Nayak
  2026-07-15  7:48 ` [RFC PATCH 1/2] cpufreq/amd-pstate: Set min_limit_freq based on bios_min_perf K Prateek Nayak
  2026-07-15  7:48 ` [RFC PATCH 2/2] cpufreq/amd-pstate: Correctly reset bios_min_perf during suspend and offlining K Prateek Nayak
@ 2026-07-16 19:50 ` Mario Limonciello
  2026-07-17  2:54   ` K Prateek Nayak
  2 siblings, 1 reply; 8+ messages in thread
From: Mario Limonciello @ 2026-07-16 19:50 UTC (permalink / raw)
  To: K Prateek Nayak, Huang Rui, Rafael J. Wysocki, Viresh Kumar
  Cc: Mario Limonciello (AMD), Perry Yuan, linux-pm, linux-kernel



On 7/15/26 02:48, K Prateek Nayak wrote:
> It was noted during internal testing that amd-pstate active mode with
> performance governor always sets the min_perf to nominal_perf despite
> BIOS having supplied bios_min_perf as the preferred idling perf.
> 
> Users who set the "Requested CPU Min frequency" from BIOS are known to
> have profiled their workload at different operating frequency to know
> the best configuration and amd-pstate should use the same as the lower
> limit when configured.
> 
> While testing the fix for above, it was noted that kexec fails to
> persist bios_min_freq even when both, the old and new kernel are aware
> of bios_min_perf.
> 
> The suspend, offlining paths switched to persisting the CPPC_REQ MSR
> state at the time of suspend / offlining with only min_perf being reset
> to bios_min_perf.
> 
> msr_init() path only accepts the configured min_perf as bios_min_perf
> when it finds rest of the bits in CPPC_REQ MSR to be 0. This is
> intentional to prevent stale value of last CPPC_REQ from kernels that
> are not aware of bios_min_perf to be mistakenly interpreted as the
> bios_min_perf during kexec boot.

Is it a real valid case we need to worry about for someone kexec'ing 
between kernels that are aware of this vs not aware of it?

During kernel development; sure this might happen.  But I would think 
once this is available in mainline any of the distro kernels will have 
picked this up and people will start with a distro kernel with support, 
or they'll start with a distro kernel without and upgrade to one with.

I can't imagine a case people will go the other way.

> 
> Work around this limitation by stashing the state of CPPC_REQ during
> suspend / offlining in cpudata and then only programming the
> bios_min_perf into CPPC_REQ while keeping the rest of the bits as 0.
> 
> Resume / onlining callbacks use the stashed state to restore the
> CPPC_REQ before the CPU resumes normal operation.
> 
> Patches are prepared on top of linux-pm:bleeding-edge at commit
> 0e4eef86f0f4d ("Merge branch 'thermal-intel' into bleeding-edge").
> 


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

* Re: [RFC PATCH 1/2] cpufreq/amd-pstate: Set min_limit_freq based on bios_min_perf
  2026-07-15  7:48 ` [RFC PATCH 1/2] cpufreq/amd-pstate: Set min_limit_freq based on bios_min_perf K Prateek Nayak
@ 2026-07-16 19:54   ` Mario Limonciello
  0 siblings, 0 replies; 8+ messages in thread
From: Mario Limonciello @ 2026-07-16 19:54 UTC (permalink / raw)
  To: K Prateek Nayak, Huang Rui, Rafael J. Wysocki, Viresh Kumar
  Cc: Mario Limonciello (AMD), Perry Yuan, linux-pm, linux-kernel



On 7/15/26 02:48, K Prateek Nayak wrote:
> amd_pstate_update_min_max_limit() sets the min_limit_perf to the
> nominal_perf to avoid frequency throttling when the system is idling.
> 
> This was found to be an ideal default but is suboptimal for users who
> have profiled their workload at different operating frequencies and have
> configured the optimal idling frequency via bios_min_perf.
> 
> Use the bios_min_perf (if configured) as the min_limit_perf when running
> with performance governor. In absence of bios_min_perf, continue using
> nominal_perf as the default min_limit_perf to avoid throttling.
> 
> Fixes: 608a76b65288 ("cpufreq/amd-pstate: Add support for the "Requested CPU Min frequency" BIOS option")
> Signed-off-by: K Prateek Nayak <kprateek.nayak@amd.com>

This patch I don't have a concern with.

Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
> ---
>   drivers/cpufreq/amd-pstate.c | 21 +++++++++++++++------
>   1 file changed, 15 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
> index f8d8288d644f8..a6e43db6efefe 100644
> --- a/drivers/cpufreq/amd-pstate.c
> +++ b/drivers/cpufreq/amd-pstate.c
> @@ -702,9 +702,12 @@ static void amd_pstate_update_min_max_limit(struct cpufreq_policy *policy)
>   	WRITE_ONCE(cpudata->max_limit_freq, policy->max);
>   
>   	if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) {
> +		u8 min_limit_perf = perf.bios_min_perf ?: perf.nominal_perf;
> +		u32 min_limit_freq;
> +
>   		/*
> -		 * For performance policy, set MinPerf to nominal_perf rather than
> -		 * highest_perf or lowest_nonlinear_perf.
> +		 * For performance policy, set MinPerf to nominal_perf / bios_min_perf
> +		 * rather than highest_perf or lowest_nonlinear_perf.
>   		 *
>   		 * Per commit 0c411b39e4f4c, using highest_perf was observed
>   		 * to cause frequency throttling on power-limited platforms, leading to
> @@ -712,11 +715,17 @@ static void amd_pstate_update_min_max_limit(struct cpufreq_policy *policy)
>   		 * performance too much for HPC workloads requiring high frequency
>   		 * operation and minimal wakeup latency from idle states.
>   		 *
> -		 * nominal_perf therefore provides a balance by avoiding throttling
> -		 * while still maintaining enough performance for HPC workloads.
> +		 * nominal_perf therefore provides a balanced default by avoiding
> +		 * throttling while still maintaining enough performance for HPC
> +		 * workloads when bios_min_perf is not available.
> +		 *
> +		 * When bios_min_perf is available, users have profiled their workloads
> +		 * to understand the best idling frequency. Use that instead.
>   		 */
> -		perf.min_limit_perf = min(perf.nominal_perf, perf.max_limit_perf);
> -		WRITE_ONCE(cpudata->min_limit_freq, min(cpudata->nominal_freq, cpudata->max_limit_freq));
> +		min_limit_freq = perf_to_freq(perf, cpudata->nominal_freq, min_limit_perf);
> +		perf.min_limit_perf = min_limit_perf;
> +
> +		WRITE_ONCE(cpudata->min_limit_freq, min(min_limit_freq, cpudata->max_limit_freq));
>   	} else {
>   		perf.min_limit_perf = freq_to_perf(perf, cpudata->nominal_freq, policy->min);
>   		WRITE_ONCE(cpudata->min_limit_freq, policy->min);


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

* Re: [RFC PATCH 2/2] cpufreq/amd-pstate: Correctly reset bios_min_perf during suspend and offlining
  2026-07-15  7:48 ` [RFC PATCH 2/2] cpufreq/amd-pstate: Correctly reset bios_min_perf during suspend and offlining K Prateek Nayak
@ 2026-07-16 19:57   ` Mario Limonciello
  0 siblings, 0 replies; 8+ messages in thread
From: Mario Limonciello @ 2026-07-16 19:57 UTC (permalink / raw)
  To: K Prateek Nayak, Huang Rui, Rafael J. Wysocki, Viresh Kumar
  Cc: Mario Limonciello (AMD), Perry Yuan, linux-pm, linux-kernel



On 7/15/26 02:48, K Prateek Nayak wrote:
> msr_init_perf() accepts the configured bios_min_perf in CPPC_REQ MSR
> only if it finds the other fields (bits[63:16] + bits[7:0]) to be 0.
> 
> This is intentional: a kernel unaware of bios_min_perf can leave the
> last perf request programmed in the CPPC_REQ MSR before kexec and the
> newer kernel, which is aware of bios_min_perf, should avoid incorrectly
> interpreting this stale value form last CPPC_REQ as the bios_min_perf.
> 
> A kernel aware of bios_min_perf is expected to reprogram the CPPC_REQ as
> seen at the time of reset during suspend / onlining. This allows for
> kexeced kernel to pick the bios_min_freq during boot without needing
> complex mechanisms like KHO.
> 
> Although suspend / offline callbacks correctly restore min_perf back to
> bios_min_perf, they fail to set other fields to 0s which causes the
> kexeced kernel to think the CPPC_REQ is configured with a stale value
> from the last boot.
> 
> Restore the CPPC_REQ seen at the time of the boot during suspend /
> offline.
> 
> The "cpudata->cppc_req_cached" is temporarily clobbered when the CPU is
> suspended / offlined to reflect the perf state at that time. A
> subsequent resume / onlining will use this clobbered data to reconstruct
> the CPPC request and force a reprogramming of hardware to restore the
> state back.
> 
> Since cpufreq is suspended at the time of clobbering, the state will
> persist, undisturbed, until the CPU resumes activity.

Another idea; how about if you use kexec_in_progress to control behavior?

> 
> Fixes: 85d7dda5a9f6 ("cpufreq/amd-pstate: Fix a regression leading to EPP 0 after hibernate")
> Fixes: ba3319e59057 ("cpufreq/amd-pstate: Fix a regression leading to EPP 0 after resume")
> Signed-off-by: K Prateek Nayak <kprateek.nayak@amd.com>
> ---
>   drivers/cpufreq/amd-pstate.c | 61 +++++++++++++++++++++++++++++-------
>   1 file changed, 50 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
> index a6e43db6efefe..6ab769d4bd2da 100644
> --- a/drivers/cpufreq/amd-pstate.c
> +++ b/drivers/cpufreq/amd-pstate.c
> @@ -2044,6 +2044,8 @@ static int amd_pstate_cpu_online(struct cpufreq_policy *policy)
>   {
>   	struct amd_cpudata *cpudata = policy->driver_data;
>   	union perf_cached perf = READ_ONCE(cpudata->perf);
> +	u64 cppc_req_cached = cpudata->cppc_req_cached;
> +	u8 min_perf, max_perf, des_perf, epp;
>   	u8 cached_floor_perf;
>   	int ret;
>   
> @@ -2051,6 +2053,22 @@ static int amd_pstate_cpu_online(struct cpufreq_policy *policy)
>   	if (ret)
>   		return ret;
>   
> +
> +	max_perf = FIELD_GET(AMD_CPPC_MAX_PERF_MASK, cppc_req_cached);
> +	des_perf = FIELD_GET(AMD_CPPC_DES_PERF_MASK, cppc_req_cached);
> +	min_perf = FIELD_GET(AMD_CPPC_MIN_PERF_MASK, cppc_req_cached);
> +	epp = FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cppc_req_cached);
> +
> +	/*
> +	 * During offline, cppc_req_cached is clobbered with the state at the time of
> +	 * hotplug. Retrieve the relevant fields and then force a mismatch for the H/W
> +	 * to update with the correct values.
> +	 */
> +	cpudata->cppc_req_cached++;
> +	ret = amd_pstate_update_perf(policy, min_perf, des_perf, max_perf, epp, false);
> +	if (ret)
> +		return ret;
> +
>   	cached_floor_perf = freq_to_perf(perf, cpudata->nominal_freq, cpudata->floor_freq);
>   	return amd_pstate_set_floor_perf(policy, cached_floor_perf);
>   }
> @@ -2059,6 +2077,7 @@ static int amd_pstate_cpu_offline(struct cpufreq_policy *policy)
>   {
>   	struct amd_cpudata *cpudata = policy->driver_data;
>   	union perf_cached perf = READ_ONCE(cpudata->perf);
> +	u64 cppc_req_cached = cpudata->cppc_req_cached;
>   	int ret;
>   
>   	/*
> @@ -2066,14 +2085,13 @@ static int amd_pstate_cpu_offline(struct cpufreq_policy *policy)
>   	 * min_perf value across kexec reboots. If this CPU is just onlined normally after this, the
>   	 * limits, epp and desired perf will get reset to the cached values in cpudata struct
>   	 */
> -	ret = amd_pstate_update_perf(policy, perf.bios_min_perf,
> -				     FIELD_GET(AMD_CPPC_DES_PERF_MASK, cpudata->cppc_req_cached),
> -				     FIELD_GET(AMD_CPPC_MAX_PERF_MASK, cpudata->cppc_req_cached),
> -				     FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cpudata->cppc_req_cached),
> -				     false);
> +	ret = amd_pstate_update_perf(policy, perf.bios_min_perf, 0U, 0U, 0U, false);
>   	if (ret)
>   		return ret;
>   
> +	/* HACK: See the comment in amd_pstate_suspend() below. */
> +	cpudata->cppc_req_cached = cppc_req_cached;
> +
>   	return amd_pstate_set_floor_perf(policy, cpudata->bios_floor_perf);
>   }
>   
> @@ -2081,6 +2099,7 @@ static int amd_pstate_suspend(struct cpufreq_policy *policy)
>   {
>   	struct amd_cpudata *cpudata = policy->driver_data;
>   	union perf_cached perf = READ_ONCE(cpudata->perf);
> +	u64 cppc_req_cached = cpudata->cppc_req_cached;
>   	int ret;
>   
>   	/*
> @@ -2088,14 +2107,17 @@ static int amd_pstate_suspend(struct cpufreq_policy *policy)
>   	 * min_perf value across kexec reboots. If this CPU is just resumed back without kexec,
>   	 * the limits, epp and desired perf will get reset to the cached values in cpudata struct
>   	 */
> -	ret = amd_pstate_update_perf(policy, perf.bios_min_perf,
> -				     FIELD_GET(AMD_CPPC_DES_PERF_MASK, cpudata->cppc_req_cached),
> -				     FIELD_GET(AMD_CPPC_MAX_PERF_MASK, cpudata->cppc_req_cached),
> -				     FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cpudata->cppc_req_cached),
> -				     false);
> +	ret = amd_pstate_update_perf(policy, perf.bios_min_perf, 0U, 0U, 0U, false);
>   	if (ret)
>   		return ret;
>   
> +	/*
> +	 * HACK: Restore the cppc_req_cached at the time of suspend. This is
> +	 * used during amd_pstate_epp_resume() to restore the correct EPP value
> +	 * when this CPU resumes.
> +	 */
> +	cpudata->cppc_req_cached = cppc_req_cached;
> +
>   	ret = amd_pstate_set_floor_perf(policy, cpudata->bios_floor_perf);
>   	if (ret)
>   		return ret;
> @@ -2131,9 +2153,26 @@ static int amd_pstate_epp_resume(struct cpufreq_policy *policy)
>   	u8 cached_floor_perf;
>   
>   	if (cpudata->suspended) {
> +		u64 cppc_req_cached = cpudata->cppc_req_cached;
> +		u8 min_perf, max_perf, des_perf, epp;
>   		int ret;
>   
> -		/* enable amd pstate from suspend state*/
> +		max_perf = FIELD_GET(AMD_CPPC_MAX_PERF_MASK, cppc_req_cached);
> +		des_perf = FIELD_GET(AMD_CPPC_DES_PERF_MASK, cppc_req_cached);
> +		min_perf = FIELD_GET(AMD_CPPC_MIN_PERF_MASK, cppc_req_cached);
> +		epp = FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cppc_req_cached);
> +
> +		/*
> +		 * Enable amd-pstate from suspend state. During suspend, cppc_req_cached
> +		 * is clobbered with the state at the time of suspend. Retrieve the
> +		 * relevant fields and then force a mismatch for the H/W to update with
> +		 * the correct values.
> +		 */
> +		cpudata->cppc_req_cached++;
> +		ret = amd_pstate_update_perf(policy, min_perf, des_perf, max_perf, epp, false);
> +		if (ret)
> +			return ret;
> +
>   		ret = amd_pstate_epp_update_limit(policy, false);
>   		if (ret)
>   			return ret;


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

* Re: [RFC PATCH 0/2] cpufreq/amd-pstate: Fixes for "Requested CPU Min frequency" support
  2026-07-16 19:50 ` [RFC PATCH 0/2] cpufreq/amd-pstate: Fixes for "Requested CPU Min frequency" support Mario Limonciello
@ 2026-07-17  2:54   ` K Prateek Nayak
  2026-07-17  4:22     ` Mario Limonciello
  0 siblings, 1 reply; 8+ messages in thread
From: K Prateek Nayak @ 2026-07-17  2:54 UTC (permalink / raw)
  To: Mario Limonciello, Huang Rui, Rafael J. Wysocki, Viresh Kumar
  Cc: Mario Limonciello (AMD), Perry Yuan, linux-pm, linux-kernel

Hello Mario,

Thank you for reviewing the series.

On 7/17/2026 1:20 AM, Mario Limonciello wrote:
> 
> 
> On 7/15/26 02:48, K Prateek Nayak wrote:
>> It was noted during internal testing that amd-pstate active mode with
>> performance governor always sets the min_perf to nominal_perf despite
>> BIOS having supplied bios_min_perf as the preferred idling perf.
>>
>> Users who set the "Requested CPU Min frequency" from BIOS are known to
>> have profiled their workload at different operating frequency to know
>> the best configuration and amd-pstate should use the same as the lower
>> limit when configured.
>>
>> While testing the fix for above, it was noted that kexec fails to
>> persist bios_min_freq even when both, the old and new kernel are aware
>> of bios_min_perf.
>>
>> The suspend, offlining paths switched to persisting the CPPC_REQ MSR
>> state at the time of suspend / offlining with only min_perf being reset
>> to bios_min_perf.
>>
>> msr_init() path only accepts the configured min_perf as bios_min_perf
>> when it finds rest of the bits in CPPC_REQ MSR to be 0. This is
>> intentional to prevent stale value of last CPPC_REQ from kernels that
>> are not aware of bios_min_perf to be mistakenly interpreted as the
>> bios_min_perf during kexec boot.
> 
> Is it a real valid case we need to worry about for someone kexec'ing between kernels that are aware of this vs not aware of it?

Since there are defensive measure in place I thought it might have
been a concern but as you said, I doubt anyone is running one of
those flavors and kexecing back and forth that often.


> During kernel development; sure this might happen.  But I would think once this is available in mainline any of the distro kernels will have picked this up and people will start with a distro kernel with support, or they'll start with a distro kernel without and upgrade to one with.
> 
> I can't imagine a case people will go the other way.

Ack! So I'm thinking of changing Patch 2 to:

diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index 27cfacd283be..707e1485eb1c 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -462,7 +462,6 @@ static int msr_init_perf(struct amd_cpudata *cpudata)
 {
 	union perf_cached perf = READ_ONCE(cpudata->perf);
 	u64 cap1, numerator, cppc_req;
-	u8 min_perf;
 
 	int ret = rdmsrq_safe_on_cpu(cpudata->cpu, MSR_AMD_CPPC_CAP1,
 				     &cap1);
@@ -478,16 +477,6 @@ static int msr_init_perf(struct amd_cpudata *cpudata)
 		return ret;
 
 	WRITE_ONCE(cpudata->cppc_req_cached, cppc_req);
-	min_perf = FIELD_GET(AMD_CPPC_MIN_PERF_MASK, cppc_req);
-
-	/*
-	 * Clear out the min_perf part to check if the rest of the MSR is 0, if yes, this is an
-	 * indication that the min_perf value is the one specified through the BIOS option
-	 */
-	cppc_req &= ~(AMD_CPPC_MIN_PERF_MASK);
-
-	if (!cppc_req)
-		perf.bios_min_perf = min_perf;
 
 	perf.highest_perf = numerator;
 	perf.max_limit_perf = numerator;
@@ -495,6 +484,7 @@ static int msr_init_perf(struct amd_cpudata *cpudata)
 	perf.nominal_perf = FIELD_GET(AMD_CPPC_NOMINAL_PERF_MASK, cap1);
 	perf.lowest_nonlinear_perf = FIELD_GET(AMD_CPPC_LOWNONLIN_PERF_MASK, cap1);
 	perf.lowest_perf = FIELD_GET(AMD_CPPC_LOWEST_PERF_MASK, cap1);
+	perf.bios_min_perf = FIELD_GET(AMD_CPPC_MIN_PERF_MASK, cppc_req);
 	WRITE_ONCE(cpudata->perf, perf);
 	WRITE_ONCE(cpudata->prefcore_ranking, FIELD_GET(AMD_CPPC_HIGHEST_PERF_MASK, cap1));
 	WRITE_ONCE(cpudata->floor_perf_cnt, FIELD_GET(AMD_CPPC_FLOOR_PERF_CNT_MASK, cap1));
@@ -1046,6 +1036,13 @@ static int amd_pstate_init_freq(struct amd_cpudata *cpudata)
 		return -EINVAL;
 	}
 
+	if (perf.bios_min_perf) {
+		u32 bios_min_freq = perf_to_freq(perf, cpudata->nominal_freq, perf.bios_min_perf);
+
+		pr_debug("Found Requested CPU Min Frequency of %u on CPU%d\n",
+			 bios_min_freq, cpudata->cpu);
+	}
+
 	return 0;
 }
 
---

It gets rid of that defensive check and leaves enough breadcrumbs to
debug any issues that might arise during a kexec. Thoughts?

-- 
Thanks and Regards,
Prateek


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

* Re: [RFC PATCH 0/2] cpufreq/amd-pstate: Fixes for "Requested CPU Min frequency" support
  2026-07-17  2:54   ` K Prateek Nayak
@ 2026-07-17  4:22     ` Mario Limonciello
  0 siblings, 0 replies; 8+ messages in thread
From: Mario Limonciello @ 2026-07-17  4:22 UTC (permalink / raw)
  To: K Prateek Nayak, Huang Rui, Rafael J. Wysocki, Viresh Kumar
  Cc: Mario Limonciello (AMD), Perry Yuan, linux-pm, linux-kernel



On 7/16/26 21:54, K Prateek Nayak wrote:
> Hello Mario,
> 
> Thank you for reviewing the series.
> 
> On 7/17/2026 1:20 AM, Mario Limonciello wrote:
>>
>>
>> On 7/15/26 02:48, K Prateek Nayak wrote:
>>> It was noted during internal testing that amd-pstate active mode with
>>> performance governor always sets the min_perf to nominal_perf despite
>>> BIOS having supplied bios_min_perf as the preferred idling perf.
>>>
>>> Users who set the "Requested CPU Min frequency" from BIOS are known to
>>> have profiled their workload at different operating frequency to know
>>> the best configuration and amd-pstate should use the same as the lower
>>> limit when configured.
>>>
>>> While testing the fix for above, it was noted that kexec fails to
>>> persist bios_min_freq even when both, the old and new kernel are aware
>>> of bios_min_perf.
>>>
>>> The suspend, offlining paths switched to persisting the CPPC_REQ MSR
>>> state at the time of suspend / offlining with only min_perf being reset
>>> to bios_min_perf.
>>>
>>> msr_init() path only accepts the configured min_perf as bios_min_perf
>>> when it finds rest of the bits in CPPC_REQ MSR to be 0. This is
>>> intentional to prevent stale value of last CPPC_REQ from kernels that
>>> are not aware of bios_min_perf to be mistakenly interpreted as the
>>> bios_min_perf during kexec boot.
>>
>> Is it a real valid case we need to worry about for someone kexec'ing between kernels that are aware of this vs not aware of it?
> 
> Since there are defensive measure in place I thought it might have
> been a concern but as you said, I doubt anyone is running one of
> those flavors and kexecing back and forth that often.
> 
> 
>> During kernel development; sure this might happen.  But I would think once this is available in mainline any of the distro kernels will have picked this up and people will start with a distro kernel with support, or they'll start with a distro kernel without and upgrade to one with.
>>
>> I can't imagine a case people will go the other way.
> 
> Ack! So I'm thinking of changing Patch 2 to:
> 
> diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
> index 27cfacd283be..707e1485eb1c 100644
> --- a/drivers/cpufreq/amd-pstate.c
> +++ b/drivers/cpufreq/amd-pstate.c
> @@ -462,7 +462,6 @@ static int msr_init_perf(struct amd_cpudata *cpudata)
>   {
>   	union perf_cached perf = READ_ONCE(cpudata->perf);
>   	u64 cap1, numerator, cppc_req;
> -	u8 min_perf;
>   
>   	int ret = rdmsrq_safe_on_cpu(cpudata->cpu, MSR_AMD_CPPC_CAP1,
>   				     &cap1);
> @@ -478,16 +477,6 @@ static int msr_init_perf(struct amd_cpudata *cpudata)
>   		return ret;
>   
>   	WRITE_ONCE(cpudata->cppc_req_cached, cppc_req);
> -	min_perf = FIELD_GET(AMD_CPPC_MIN_PERF_MASK, cppc_req);
> -
> -	/*
> -	 * Clear out the min_perf part to check if the rest of the MSR is 0, if yes, this is an
> -	 * indication that the min_perf value is the one specified through the BIOS option
> -	 */
> -	cppc_req &= ~(AMD_CPPC_MIN_PERF_MASK);
> -
> -	if (!cppc_req)
> -		perf.bios_min_perf = min_perf;
>   
>   	perf.highest_perf = numerator;
>   	perf.max_limit_perf = numerator;
> @@ -495,6 +484,7 @@ static int msr_init_perf(struct amd_cpudata *cpudata)
>   	perf.nominal_perf = FIELD_GET(AMD_CPPC_NOMINAL_PERF_MASK, cap1);
>   	perf.lowest_nonlinear_perf = FIELD_GET(AMD_CPPC_LOWNONLIN_PERF_MASK, cap1);
>   	perf.lowest_perf = FIELD_GET(AMD_CPPC_LOWEST_PERF_MASK, cap1);
> +	perf.bios_min_perf = FIELD_GET(AMD_CPPC_MIN_PERF_MASK, cppc_req);
>   	WRITE_ONCE(cpudata->perf, perf);
>   	WRITE_ONCE(cpudata->prefcore_ranking, FIELD_GET(AMD_CPPC_HIGHEST_PERF_MASK, cap1));
>   	WRITE_ONCE(cpudata->floor_perf_cnt, FIELD_GET(AMD_CPPC_FLOOR_PERF_CNT_MASK, cap1));
> @@ -1046,6 +1036,13 @@ static int amd_pstate_init_freq(struct amd_cpudata *cpudata)
>   		return -EINVAL;
>   	}
>   
> +	if (perf.bios_min_perf) {
> +		u32 bios_min_freq = perf_to_freq(perf, cpudata->nominal_freq, perf.bios_min_perf);
> +
> +		pr_debug("Found Requested CPU Min Frequency of %u on CPU%d\n",
> +			 bios_min_freq, cpudata->cpu);
> +	}
> +
>   	return 0;
>   }
>   
> ---
> 
> It gets rid of that defensive check and leaves enough breadcrumbs to
> debug any issues that might arise during a kexec. Thoughts?
> 

That looks better to me, yes.


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

end of thread, other threads:[~2026-07-17  4:23 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-15  7:48 [RFC PATCH 0/2] cpufreq/amd-pstate: Fixes for "Requested CPU Min frequency" support K Prateek Nayak
2026-07-15  7:48 ` [RFC PATCH 1/2] cpufreq/amd-pstate: Set min_limit_freq based on bios_min_perf K Prateek Nayak
2026-07-16 19:54   ` Mario Limonciello
2026-07-15  7:48 ` [RFC PATCH 2/2] cpufreq/amd-pstate: Correctly reset bios_min_perf during suspend and offlining K Prateek Nayak
2026-07-16 19:57   ` Mario Limonciello
2026-07-16 19:50 ` [RFC PATCH 0/2] cpufreq/amd-pstate: Fixes for "Requested CPU Min frequency" support Mario Limonciello
2026-07-17  2:54   ` K Prateek Nayak
2026-07-17  4:22     ` Mario Limonciello

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