From: Aiden Hecker <heckeraiden@gmail.com>
To: "Huang Rui" <ray.huang@amd.com>,
"Mario Limonciello" <mario.limonciello@amd.com>,
"Perry Yuan" <perry.yuan@amd.com>,
"K Prateek Nayak" <kprateek.nayak@amd.com>,
"Rafael J . Wysocki" <rafael@kernel.org>,
"Viresh Kumar" <viresh.kumar@linaro.org>,
"Hans de Goede" <hansg@kernel.org>,
"Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>
Cc: Ingo Molnar <mingo@redhat.com>,
Peter Zijlstra <peterz@infradead.org>,
linux-pm@vger.kernel.org, platform-driver-x86@vger.kernel.org,
linux-kernel@vger.kernel.org,
Aiden Hecker <heckeraiden@gmail.com>
Subject: [PATCH v2 1/2] cpufreq: amd-pstate: Enable hybrid CPU capacity scaling on heterogeneous Zen CPUs
Date: Thu, 12 Nov 2026 18:29:33 -0800 [thread overview]
Message-ID: <20261113022934.13397-2-heckeraiden@gmail.com> (raw)
In-Reply-To: <20261113022934.13397-1-heckeraiden@gmail.com>
On heterogeneous AMD Zen 5 / Zen 5c processors (X86_FEATURE_AMD_HTR_CORES,
such as Ryzen AI 7 350 Family 1Ah Model 60h), physical Zen 5 cores
(5.09 GHz, CPPC highest_perf 196..208) and dense Zen 5c cores (3.51 GHz,
CPPC highest_perf 135) alternate inside a single shared 16 MiB L3 CCX.
Currently amd-pstate records prefcore_ranking and nominal_perf per CPU,
and disables amd_pstate_prefcore when amd_hfi is active, but neither
driver initializes x86 hybrid capacity scaling. As a result, all logical
CPUs remain at uniform capacity 1024 across the shared L3 domain.
Record per-CPU highest_perf and nominal_perf when X86_FEATURE_AMD_HTR_CORES
is present and program arch_enable_hybrid_capacity_scale(),
arch_set_cpu_capacity(), and topology_set_cpu_scale() once asymmetry is
observed across online CPUs. SMT siblings are explicitly synchronized to
the same capacity value so SD_SHARE_CPUCAPACITY never triggers asymmetric
SMT warnings, while rebuild_sched_domains() is deferred to a workqueue.
Developed with coding assistance from Antigravity (Google DeepMind).
Tested-by: Aiden Hecker <heckeraiden@gmail.com>
Signed-off-by: Aiden Hecker <heckeraiden@gmail.com>
---
drivers/cpufreq/amd-pstate.c | 121 +++++++++++++++++++++++++++++++++++
1 file changed, 121 insertions(+)
diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index 4bedfbfc1..3f4b6a2b0 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -36,9 +36,13 @@
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
+#include <linux/cleanup.h>
+#include <linux/cpuset.h>
+#include <linux/mutex.h>
#include <linux/power_supply.h>
#include <linux/static_call.h>
#include <linux/topology.h>
+#include <linux/workqueue.h>
#include <acpi/processor.h>
#include <acpi/cppc_acpi.h>
@@ -88,6 +92,23 @@ static int cppc_state = AMD_PSTATE_UNDEFINED;
static bool amd_pstate_prefcore = true;
static struct quirk_entry *quirks;
+struct amd_hybrid_cpu_cap {
+ u32 highest_perf;
+ u32 nominal_perf;
+};
+
+static DEFINE_PER_CPU(struct amd_hybrid_cpu_cap, amd_hybrid_caps);
+static DEFINE_MUTEX(amd_hybrid_cap_lock);
+static u32 amd_hybrid_max_perf;
+static bool amd_hybrid_cap_enabled;
+
+static void amd_hybrid_cap_workfn(struct work_struct *work)
+{
+ rebuild_sched_domains();
+}
+
+static DECLARE_WORK(amd_hybrid_cap_work, amd_hybrid_cap_workfn);
+
/*
* The numeric EPP value programmed for each named preference. First dimension
* is CPU type (TOPO_CPU_TYPE_ANY for non-hybrid, TOPO_CPU_TYPE_PERFORMANCE/
@@ -969,6 +990,99 @@ static void amd_pstate_init_prefcore(struct amd_cpudata *cpudata)
sched_set_itmt_core_prio((int)READ_ONCE(cpudata->prefcore_ranking), cpudata->cpu);
}
+static void amd_pstate_record_hybrid_cap(struct amd_cpudata *cpudata)
+{
+ struct amd_hybrid_cpu_cap *cap;
+ union perf_cached perf;
+ u32 highest_perf;
+
+ if (!cpu_feature_enabled(X86_FEATURE_AMD_HTR_CORES))
+ return;
+
+ perf = READ_ONCE(cpudata->perf);
+ highest_perf = READ_ONCE(cpudata->prefcore_ranking);
+ if (!highest_perf)
+ highest_perf = perf.highest_perf;
+
+ guard(mutex)(&amd_hybrid_cap_lock);
+ cap = per_cpu_ptr(&amd_hybrid_caps, cpudata->cpu);
+ cap->highest_perf = highest_perf;
+ cap->nominal_perf = perf.nominal_perf;
+}
+
+static void amd_pstate_sync_hybrid_capacity(void)
+{
+ u32 max_perf = 0, min_perf = U32_MAX;
+ bool updated = false;
+ int cpu;
+
+ if (!cpu_feature_enabled(X86_FEATURE_AMD_HTR_CORES))
+ return;
+
+ guard(mutex)(&amd_hybrid_cap_lock);
+
+ for_each_online_cpu(cpu) {
+ const struct cpumask *sib_mask = topology_sibling_cpumask(cpu);
+ u32 sib_max = 0;
+ int sib;
+
+ /*
+ * SMT siblings share SD_SHARE_CPUCAPACITY and must expose identical
+ * arch_scale_cpu_capacity() values to avoid scheduler warnings.
+ */
+ for_each_cpu(sib, sib_mask) {
+ u32 p = per_cpu(amd_hybrid_caps, sib).highest_perf;
+
+ if (p > sib_max)
+ sib_max = p;
+ }
+ if (!sib_max)
+ continue;
+
+ for_each_cpu(sib, sib_mask)
+ per_cpu(amd_hybrid_caps, sib).highest_perf = sib_max;
+
+ if (sib_max > max_perf)
+ max_perf = sib_max;
+ if (sib_max < min_perf)
+ min_perf = sib_max;
+ }
+
+ if (!max_perf || min_perf >= max_perf)
+ return;
+
+ if (!amd_hybrid_cap_enabled) {
+ if (!arch_enable_hybrid_capacity_scale())
+ return;
+ amd_hybrid_cap_enabled = true;
+ updated = true;
+ }
+
+ if (max_perf != amd_hybrid_max_perf) {
+ amd_hybrid_max_perf = max_perf;
+ updated = true;
+ }
+
+ for_each_online_cpu(cpu) {
+ struct amd_hybrid_cpu_cap info = per_cpu(amd_hybrid_caps, cpu);
+ unsigned long prev_cap;
+
+ if (!info.highest_perf || !info.nominal_perf)
+ continue;
+
+ prev_cap = arch_scale_cpu_capacity(cpu);
+ arch_set_cpu_capacity(cpu, info.highest_perf, max_perf,
+ info.highest_perf, info.nominal_perf);
+ topology_set_cpu_scale(cpu, arch_scale_cpu_capacity(cpu));
+
+ if (arch_scale_cpu_capacity(cpu) != prev_cap)
+ updated = true;
+ }
+
+ if (updated)
+ schedule_work(&amd_hybrid_cap_work);
+}
+
static void amd_pstate_update_limits(struct cpufreq_policy *policy)
{
struct amd_cpudata *cpudata;
@@ -1130,6 +1244,7 @@ static int amd_pstate_cpu_init(struct cpufreq_policy *policy)
if (ret)
goto free_cpudata1;
+ amd_pstate_record_hybrid_cap(cpudata);
amd_pstate_init_prefcore(cpudata);
ret = amd_pstate_init_freq(cpudata);
@@ -1769,11 +1884,14 @@ static int amd_pstate_register_driver(int mode)
if (amd_pstate_prefcore)
sched_set_itmt_support();
+ amd_pstate_sync_hybrid_capacity();
+
return 0;
}
static int amd_pstate_unregister_driver(int dummy)
{
+ cancel_work_sync(&amd_hybrid_cap_work);
cpufreq_unregister_driver(current_pstate_driver);
amd_pstate_driver_cleanup();
return 0;
@@ -1985,6 +2103,7 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
if (ret)
goto free_cpudata1;
+ amd_pstate_record_hybrid_cap(cpudata);
amd_pstate_init_prefcore(cpudata);
ret = amd_pstate_init_freq(cpudata);
@@ -2149,6 +2268,8 @@ static int amd_pstate_cpu_online(struct cpufreq_policy *policy)
return ret;
cached_floor_perf = freq_to_perf(perf, cpudata->nominal_freq, cpudata->floor_freq);
+ amd_pstate_record_hybrid_cap(cpudata);
+ amd_pstate_sync_hybrid_capacity();
return amd_pstate_set_floor_perf(policy, cached_floor_perf);
}
--
2.56.1
next prev parent reply other threads:[~2026-11-13 2:29 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-13 2:29 [PATCH v2 0/2] cpufreq: amd-pstate, platform/x86/amd/hfi: Enable hybrid capacity scaling & SMT ITMT ordering on Zen 5 / Zen 5c Aiden Hecker
2026-11-13 2:29 ` Aiden Hecker [this message]
2026-11-13 2:29 ` [PATCH v2 2/2] platform/x86/amd/hfi: Rank SMT siblings below primary cores for ITMT packing Aiden Hecker
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=20261113022934.13397-2-heckeraiden@gmail.com \
--to=heckeraiden@gmail.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=mario.limonciello@amd.com \
--cc=mingo@redhat.com \
--cc=perry.yuan@amd.com \
--cc=peterz@infradead.org \
--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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.