mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Mario Limonciello <mario.limonciello@amd.com>
To: Vishal Badole <Vishal.Badole@amd.com>,
	ray.huang@amd.com, rafael@kernel.org, viresh.kumar@linaro.org,
	perry.yuan@amd.com, kprateek.nayak@amd.com, hansg@kernel.org,
	ilpo.jarvinen@linux.intel.com,
	platform-driver-x86@vger.kernel.org, linux-pm@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH 1/2] cpufreq: amd-pstate: Set asymmetric CPU capacity on hybrid platforms
Date: Sun, 15 Nov 2026 09:54:48 -0600	[thread overview]
Message-ID: <ea92861f-cdf6-4828-9292-b49ad6c21590@amd.com> (raw)
In-Reply-To: <20261114150310.1377654-2-Vishal.Badole@amd.com>



On 11/14/26 09:03, Vishal Badole wrote:
> AMD heterogeneous processors combine multiple core types (e.g.
> performance and efficiency cores) whose maximum sustained performance
> and frequency differ. On such platforms the Linux scheduler relies on
> per-CPU capacity values to place and balance tasks across cores.
> 
> Today amd-pstate does not export any per-CPU capacity information. As a
> result the scheduler sees every CPU as having identical capacity and
> treats performance and efficiency cores as interchangeable. Tasks are
> therefore placed and balanced without regard to core strength, which
> hurts both throughput and energy efficiency on hybrid systems.
> 
> This happens because the scheduler defaults to a uniform
> SCHED_CAPACITY_SCALE when no arch- or driver-supplied capacity exists,
> and amd-pstate never feeds the asymmetry it knows (via CPPC highest
> frequency or the effective highest performance register) into the arch
> hybrid capacity scaling interface.
> 
> Enable capacity aware scheduling on AMD heterogeneous-core processors.
> Use CPU 0 as the capacity reference, assuming it is a highest-performance
> CPU on the supported platforms. Cache its maximum frequency in kHz and
> effective highest performance once before driver registration, treating
> these reference capabilities as fixed for the lifetime of the system.
> During per-CPU initialization, compute each CPU's capacity relative to the
> cached reference and publish it:
> 
>   - When maximum-frequency data is available for both CPUs, scale by
> 	 frequency via arch_set_cpu_capacity().
>   - Otherwise, fall back to performance-register based scaling using the
>     effective highest_perf values.
> 
> Record frequency availability before applying the per-CPU fallback. Keep
> frequency and performance references separate instead of interpreting a
> boost-ratio numerator in performance units as MHz. The fallback base
> frequency still depends on each CPU's nominal frequency and performance,
> so calculate that conversion per CPU. Disable CAS if reference setup fails
> without preventing normal CPUFreq registration.
> 
> Capacity aware scheduling is enabled by default on supported hardware and
> can be turned off with the "amd_cas=disable" boot parameter. Its effective
> state is exposed through the "cas" sysfs attribute, and also exported via
> amd_pstate_cas_active() so amd-hfi can stand down when capacity aware
> scheduling owns the heterogeneous cores.
> 
> Signed-off-by: Vishal Badole <Vishal.Badole@amd.com>
> ---
>   MAINTAINERS                  |   1 +
>   drivers/cpufreq/amd-pstate.c | 102 ++++++++++++++++++++++++++++++++++-
>   include/linux/amd-pstate.h   |  14 +++++
>   3 files changed, 115 insertions(+), 2 deletions(-)
>   create mode 100644 include/linux/amd-pstate.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 222b7fc2c3aa..26217272be95 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1324,6 +1324,7 @@ L:	linux-pm@vger.kernel.org
>   S:	Supported
>   F:	Documentation/admin-guide/pm/amd-pstate.rst
>   F:	drivers/cpufreq/amd-pstate*
> +F:	include/linux/amd-pstate.h
>   F:	tools/power/x86/amd_pstate_tracer/amd_pstate_trace.py
>   
>   AMD PTDMA DRIVER
> diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
> index 4bedfbfc18b6..59eb2c4c7867 100644
> --- a/drivers/cpufreq/amd-pstate.c
> +++ b/drivers/cpufreq/amd-pstate.c
> @@ -29,6 +29,7 @@
>   #include <linux/smp.h>
>   #include <linux/sched.h>
>   #include <linux/cpufreq.h>
> +#include <linux/sched/smt.h>
>   #include <linux/compiler.h>
>   #include <linux/dmi.h>
>   #include <linux/slab.h>
> @@ -39,6 +40,8 @@
>   #include <linux/power_supply.h>
>   #include <linux/static_call.h>
>   #include <linux/topology.h>
> +#include <linux/cpuset.h>
> +#include <linux/amd-pstate.h>
>   
>   #include <acpi/processor.h>
>   #include <acpi/cppc_acpi.h>
> @@ -86,6 +89,10 @@ static struct cpufreq_driver amd_pstate_driver;
>   static struct cpufreq_driver amd_pstate_epp_driver;
>   static int cppc_state = AMD_PSTATE_UNDEFINED;
>   static bool amd_pstate_prefcore = true;
> +static bool amd_pstate_cas = true;

As this strictly requires SMP support, what do you think about this 
instead to pick the default.

static bool amd_pstate_cas = IS_ENABLED(CONFIG_SMP);

> +static bool amd_pstate_cas_initialized;
> +static u32 amd_pstate_ref_freq_khz;
> +static u32 amd_pstate_ref_highest_perf;
>   static struct quirk_entry *quirks;
>   
>   /*
> @@ -957,8 +964,8 @@ static void amd_pstate_init_prefcore(struct amd_cpudata *cpudata)
>   		return;
>   
>   	/* should use amd-hfi instead */
> -	if (cpu_feature_enabled(X86_FEATURE_AMD_WORKLOAD_CLASS) &&
> -	    IS_ENABLED(CONFIG_AMD_HFI)) {
> +	if ((cpu_feature_enabled(X86_FEATURE_AMD_WORKLOAD_CLASS) &&
> +	     IS_ENABLED(CONFIG_AMD_HFI)) || amd_pstate_cas) {
>   		amd_pstate_prefcore = false;
>   		return;
>   	}
> @@ -969,6 +976,64 @@ static void amd_pstate_init_prefcore(struct amd_cpudata *cpudata)
>   	sched_set_itmt_core_prio((int)READ_ONCE(cpudata->prefcore_ranking), cpudata->cpu);
>   }
>   
> +bool amd_pstate_cas_active(void)
> +{
> +	return READ_ONCE(amd_pstate_cas_initialized);
> +}
> +EXPORT_SYMBOL_GPL(amd_pstate_cas_active);
> +
> +static bool __init amd_pstate_init_cas(void)
> +{
> +	int highest_perf, max_freq;
> +
> +	highest_perf = amd_get_effective_highest_perf(0);
> +	if (highest_perf <= 0) {
> +		pr_warn("Invalid reference highest performance (%d)\n", highest_perf);
> +		return false;
> +	}
> +
> +	max_freq = amd_get_max_frequency(0);
> +	if (max_freq < 0 || (u32)max_freq > U32_MAX / 1000) {
> +		pr_warn("Invalid reference maximum frequency (%d)\n", max_freq);
> +		return false;
> +	}
> +
> +	amd_pstate_ref_highest_perf = highest_perf;
> +	amd_pstate_ref_freq_khz = max_freq * 1000U;
> +
> +	return arch_enable_hybrid_capacity_scale();
> +}
> +
> +static void amd_pstate_set_cpu_capacity(struct amd_cpudata *cpudata,
> +					bool highest_freq_available)
> +{
> +	unsigned long capacity, max_capacity, capacity_freq, base_freq;
> +
> +	if (!IS_ENABLED(CONFIG_SMP))
> +		return;

 From what I can tell; I don't think this is realistically something 
that can get called since you already check to enable CAS based on this 
same check (IS_ENABLED(CONFIG_SMP)).

So please drop this check here.  More later about IS_ENABLED(CONFIG_SMP).

> +
> +	if (highest_freq_available && amd_pstate_ref_freq_khz) {
> +		capacity = cpudata->max_freq;
> +		max_capacity = amd_pstate_ref_freq_khz;
> +		capacity_freq = capacity;
> +		base_freq = max_capacity;
> +	} else {
> +		union perf_cached perf = READ_ONCE(cpudata->perf);
> +
> +		capacity = perf.highest_perf;
> +		max_capacity = amd_pstate_ref_highest_perf;
> +		capacity_freq = perf_to_freq(perf, cpudata->nominal_freq, perf.highest_perf);
> +		base_freq = perf_to_freq(perf, cpudata->nominal_freq, max_capacity);
> +	}
> +
> +	guard(mutex)(&sched_domains_mutex);
> +	arch_set_cpu_capacity(cpudata->cpu, capacity, max_capacity,
> +			      capacity_freq, base_freq);
> +	topology_set_cpu_scale(cpudata->cpu, arch_scale_cpu_capacity(cpudata->cpu));
> +	x86_topology_update = true;
> +	WRITE_ONCE(amd_pstate_cas_initialized, true);
> +}
> +
>   static void amd_pstate_update_limits(struct cpufreq_policy *policy)
>   {
>   	struct amd_cpudata *cpudata;
> @@ -1043,6 +1108,7 @@ static int amd_pstate_init_freq(struct amd_cpudata *cpudata)
>   	u32 min_freq, max_freq, nominal_freq, lowest_nonlinear_freq;
>   	struct cppc_perf_caps cppc_perf;
>   	union perf_cached perf;
> +	bool highest_freq_available;
>   	int ret;
>   
>   	ret = cppc_get_perf_caps(cpudata->cpu, &cppc_perf);
> @@ -1069,6 +1135,7 @@ static int amd_pstate_init_freq(struct amd_cpudata *cpudata)
>   
>   	/* try to look up from BIOS/quirk first, fall back to (nominal_freq * highest_perf)/nominal_perf */
>   	max_freq = amd_get_max_frequency(cpudata->cpu) * 1000;
> +	highest_freq_available = !!max_freq;
>   	if (!max_freq)
>   		max_freq = perf_to_freq(perf, nominal_freq, perf.highest_perf);
>   	WRITE_ONCE(cpudata->max_freq, max_freq);
> @@ -1100,6 +1167,9 @@ static int amd_pstate_init_freq(struct amd_cpudata *cpudata)
>   			 bios_min_freq, cpudata->cpu);
>   	}
>   
> +	if (amd_pstate_cas)
> +		amd_pstate_set_cpu_capacity(cpudata, highest_freq_available);
> +
>   	return 0;
>   }
>   
> @@ -1923,12 +1993,20 @@ static ssize_t prefcore_show(struct device *dev,
>   	return sysfs_emit(buf, "%s\n", str_enabled_disabled(amd_pstate_prefcore));
>   }
>   
> +static ssize_t cas_show(struct device *dev,
> +			struct device_attribute *attr, char *buf)
> +{
> +	return sysfs_emit(buf, "%s\n", str_enabled_disabled(amd_pstate_cas_active()));
> +}
> +
>   static DEVICE_ATTR_RW(status);
>   static DEVICE_ATTR_RO(prefcore);
> +static DEVICE_ATTR_RO(cas);
>   
>   static struct attribute *pstate_global_attributes[] = {
>   	&dev_attr_status.attr,
>   	&dev_attr_prefcore.attr,
> +	&dev_attr_cas.attr,
>   	NULL
>   };
>   
> @@ -2438,12 +2516,23 @@ static int __init amd_pstate_init(void)
>   			return ret;
>   	}
>   
> +	amd_pstate_cas = amd_pstate_cas && IS_ENABLED(CONFIG_SMP) &&
> +			cpu_feature_enabled(X86_FEATURE_AMD_HTR_CORES);
> +
> +	/* Enable CAS for processors with heterogeneous cores. */
> +	if (amd_pstate_cas)
> +		amd_pstate_cas = amd_pstate_init_cas();
> +

This seem to be a pointless variable initialization, especially as it's 
a global variable and this runs on non-hetero systems too.  Maybe 
something like this:

0) See my previous comment about where to use IS_ENABLED(CONFIG_SMP).

1) Change amd_pstate_init_cas return type to void.

 > +static void __init amd_pstate_init_cas(void)

2) Add checks into the function like this:

static void __init amd_pstate_init_cas(void) {
	if (!amd_pstate_cas)
		return;
	if (!cpu_feature_enabled(X86_FEATURE_AMD_HTR_CORES))
		return;
	.
	.
	.
	amd_pstate_cas = arch_enable_hybrid_capacity_scale();
	
}

3) Change the call in this function to just:
amd_pstate_init_cas();

That should help avoid some extra variable writes.

>   	ret = amd_pstate_register_driver(cppc_state);
>   	if (ret) {
>   		pr_err("failed to register with return %d\n", ret);
>   		return ret;
>   	}
>   
> +	/* Per-CPU capacities are set during registration; rebuild domains once. */
> +	if (amd_pstate_cas)
> +		rebuild_sched_domains();
> +
>   	dev_root = bus_get_dev_root(&cpu_subsys);
>   	if (dev_root) {
>   		ret = sysfs_create_group(&dev_root->kobj, &amd_pstate_global_attr_group);
> @@ -2484,8 +2573,17 @@ static int __init amd_prefcore_param(char *str)
>   	return 0;
>   }
>   
> +static int __init amd_cas_param(char *str)
> +{
> +	if (!strcmp(str, "disable"))
> +		amd_pstate_cas = false;
> +
> +	return 0;
> +}
> +
>   early_param("amd_pstate", amd_pstate_param);
>   early_param("amd_prefcore", amd_prefcore_param);
> +early_param("amd_cas", amd_cas_param);
>   
>   MODULE_AUTHOR("Huang Rui <ray.huang@amd.com>");
>   MODULE_DESCRIPTION("AMD Processor P-state Frequency Driver");
> diff --git a/include/linux/amd-pstate.h b/include/linux/amd-pstate.h
> new file mode 100644
> index 000000000000..eb750abd20f7
> --- /dev/null
> +++ b/include/linux/amd-pstate.h
> @@ -0,0 +1,14 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Public interface for the AMD P-state driver.
> + */
> +#ifndef __LINUX_AMD_PSTATE_H__
> +#define __LINUX_AMD_PSTATE_H__
> +
> +#if IS_ENABLED(CONFIG_X86_AMD_PSTATE)
> +bool amd_pstate_cas_active(void);
> +#else
> +static inline bool amd_pstate_cas_active(void) { return false; }
> +#endif
> +
> +#endif /* __LINUX_AMD_PSTATE_H__ */


  reply	other threads:[~2026-11-15 15:54 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-14 15:03 [PATCH 0/2] cpufreq: amd-pstate: Capacity Aware Scheduling " Vishal Badole
2026-11-14 15:03 ` [PATCH 1/2] cpufreq: amd-pstate: Set asymmetric CPU capacity " Vishal Badole
2026-11-15 15:54   ` Mario Limonciello [this message]
2026-11-14 15:03 ` [PATCH 2/2] platform/x86/amd: hfi: Stand down when amd-pstate CAS is active Vishal Badole
2026-11-15 14:16   ` Mario Limonciello
2026-11-15 14:22     ` Mario Limonciello

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ea92861f-cdf6-4828-9292-b49ad6c21590@amd.com \
    --to=mario.limonciello@amd.com \
    --cc=Vishal.Badole@amd.com \
    --cc=hansg@kernel.org \
    --cc=ilpo.jarvinen@linux.intel.com \
    --cc=kprateek.nayak@amd.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=perry.yuan@amd.com \
    --cc=platform-driver-x86@vger.kernel.org \
    --cc=rafael@kernel.org \
    --cc=ray.huang@amd.com \
    --cc=viresh.kumar@linaro.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®