From 6fd9be0b7b2e957d24b0490b989658c4d18d92c7 Mon Sep 17 00:00:00 2001 From: Lifeng Zheng Date: Wed, 19 Nov 2025 16:13:56 +0800 Subject: [PATCH] arm64: topology: Handle AMU FIE setup on CPU hotplug Currently, when a cpufreq policy is created, the AMU FIE setup process checks all CPUs in the policy -- including those that are offline. If any of these CPUs are offline at that time, their AMU capability flag hasn't been verified yet, leading the check fail. As a result, AMU FIE is not enabled, even if the CPUs that are online do support it. Later, when the previously offline CPUs come online and report AMU support, there's no mechanism in place to re-enable AMU FIE for the policy. This leaves the entire frequency domain without AMU FIE, despite being eligible. Restrict the initial AMU FIE check to only those CPUs that are online at the time the policy is created, and allow CPUs that come online later to join the policy with AMU FIE enabled. Signed-off-by: Lifeng Zheng Acked-by: Beata Michalska Signed-off-by: Will Deacon --- arch/arm64/kernel/topology.c | 65 ++++++++++++++++++++++++++++++++++-- drivers/base/arch_topology.c | 9 ++++- 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c index cf9bb761af3a2..539b389351828 100644 --- a/arch/arm64/kernel/topology.c +++ b/arch/arm64/kernel/topology.c @@ -284,7 +284,7 @@ static int init_amu_fie_callback(struct notifier_block *nb, unsigned long val, struct cpufreq_policy *policy = data; if (val == CPUFREQ_CREATE_POLICY) - amu_fie_setup(policy->related_cpus); + amu_fie_setup(policy->cpus); /* * We don't need to handle CPUFREQ_REMOVE_POLICY event as the AMU @@ -303,10 +303,71 @@ static struct notifier_block init_amu_fie_notifier = { .notifier_call = init_amu_fie_callback, }; +static int cpuhp_topology_online(unsigned int cpu) +{ + struct cpufreq_policy *policy = cpufreq_cpu_policy(cpu); + + /* Those are cheap checks */ + + /* + * Skip this CPU if: + * - it has no cpufreq policy assigned yet, + * - no policy exists that spans CPUs with AMU counters, or + * - it was already handled. + */ + if (unlikely(!policy) || !cpumask_available(amu_fie_cpus) || + cpumask_test_cpu(cpu, amu_fie_cpus)) + return 0; + + /* + * Only proceed if all already-online CPUs in this policy + * support AMU counters. + */ + if (unlikely(!cpumask_subset(policy->cpus, amu_fie_cpus))) + return 0; + + /* + * If the new online CPU cannot pass this check, all the CPUs related to + * the same policy should be clear from amu_fie_cpus mask, otherwise they + * may use different source of the freq scale. + */ + if (!freq_counters_valid(cpu)) { + pr_warn("CPU[%u] doesn't support AMU counters\n", cpu); + topology_clear_scale_freq_source(SCALE_FREQ_SOURCE_ARCH, + policy->related_cpus); + cpumask_andnot(amu_fie_cpus, amu_fie_cpus, policy->related_cpus); + return 0; + } + + cpumask_set_cpu(cpu, amu_fie_cpus); + + topology_set_scale_freq_source(&amu_sfd, cpumask_of(cpu)); + + pr_debug("CPU[%u]: counter will be used for FIE.", cpu); + + return 0; +} + static int __init init_amu_fie(void) { - return cpufreq_register_notifier(&init_amu_fie_notifier, + int ret; + + ret = cpufreq_register_notifier(&init_amu_fie_notifier, CPUFREQ_POLICY_NOTIFIER); + if (ret) + return ret; + + ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, + "arm64/topology:online", + cpuhp_topology_online, + NULL); + if (ret < 0) { + cpufreq_unregister_notifier(&init_amu_fie_notifier, + CPUFREQ_POLICY_NOTIFIER); + return ret; + } + + return 0; } core_initcall(init_amu_fie); diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index 84ec92bff642c..c0ef6ea9c111e 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -34,7 +34,14 @@ EXPORT_PER_CPU_SYMBOL_GPL(capacity_freq_ref); static bool supports_scale_freq_counters(const struct cpumask *cpus) { - return cpumask_subset(cpus, &scale_freq_counters_mask); + int i; + + for_each_cpu(i, cpus) { + if (cpumask_test_cpu(i, &scale_freq_counters_mask)) + return true; + } + + return false; } bool topology_scale_freq_invariant(void) -- 2.47.3