diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c index cf9bb761af3a..539b38935182 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 84ec92bff642..c0ef6ea9c111 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)