Re: [PATCH v5 3/3] arch_topology: Add topology_update_cpu_capacity() for runtime updates
Christian Loehle <[email protected]>
| Newsgroups | dev.linux.lists.driver-core,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pm |
|---|---|
| Message-ID | <[email protected]> |
On 8/7/26 07:08, Xueqin Luo wrote: > When the CPPC Highest Performance register changes at runtime > (e.g. via ACPI Notify(0x85)), the scheduler's view of CPU capacity > and the frequency invariance engine's reference values become stale, > as topology_init_cpu_capacity_cppc() is only called once during boot. > > Keep raw_capacity allocated after CPPC init instead of freeing it, > and introduce topology_update_cpu_capacity() to update per-CPU > raw_capacity, capacity_freq_ref, and the normalized CPU capacity > scale at runtime. Provide a no-op stub when GENERIC_ARCH_TOPOLOGY > is disabled so cppc_cpufreq can link on those configs. Skip updates > when the value is unchanged and reject a zero capacity_scale to > avoid division by zero. > > Call this from cppc_cpufreq_update_limits() for every CPU in the > policy so shared-policy Notify(0x85) targeting a non-policy CPU > still refreshes the correct topology capacity. > > Signed-off-by: Xueqin Luo <[email protected]> > --- > drivers/base/arch_topology.c | 76 ++++++++++++++++++++++++++++++++++ > drivers/cpufreq/cppc_cpufreq.c | 2 + > include/linux/arch_topology.h | 13 ++++++ > 3 files changed, 91 insertions(+) > > diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c > index 8c5e47c28d9a..27f2bfa9f326 100644 > --- a/drivers/base/arch_topology.c > +++ b/drivers/base/arch_topology.c > @@ -229,6 +229,7 @@ static void update_topology_flags_workfn(struct work_struct *work) > } > > static u32 *raw_capacity; > +static DEFINE_MUTEX(raw_capacity_lock); > > static int free_raw_capacity(void) > { > @@ -372,13 +373,88 @@ static inline void topology_init_cpu_capacity_cppc(void) > schedule_work(&update_topology_flags_work); > pr_debug("cpu_capacity: cpu_capacity initialization done\n"); > > + /* > + * Keep raw_capacity for runtime updates via > + * topology_update_cpu_capacity(). > + */ > + return; > + > exit: > free_raw_capacity(); > } > + > void acpi_processor_init_invariance_cppc(void) > { > topology_init_cpu_capacity_cppc(); > } > + > +/** > + * topology_update_cpu_capacity - Update CPU capacity after highest_perf change > + * @cpu: CPU whose highest performance changed > + * @perf_caps: Updated CPPC performance capabilities for @cpu > + * > + * When the CPPC Highest Performance register changes at runtime > + * (e.g. via Notify(0x85)), the scheduler's view of CPU capacity > + * and the frequency invariance engine's reference values become > + * stale. This function updates the per-CPU raw_capacity, > + * capacity_freq_ref and freq_inv max ratio, then re-normalizes the > + * CPU capacity scale for all possible CPUs and triggers a sched > + * domain rebuild. If the value is unchanged, everything is skipped. > + */ > +void topology_update_cpu_capacity(unsigned int cpu, > + struct cppc_perf_caps *perf_caps) > +{ > + u32 highest_perf = perf_caps->highest_perf; > + u64 capacity, capacity_scale = 0; > + int c; > + > + guard(mutex)(&raw_capacity_lock); > + > + if (!raw_capacity || cpu >= num_possible_cpus()) > + return; > + > + /* > + * Validate: highest_perf must be >= nominal_perf and >= lowest_perf, > + * consistent with the boot-time check in topology_init_cpu_capacity_cppc(). > + */ > + if (highest_perf < perf_caps->lowest_perf) { > + pr_warn("cpu_capacity: CPU%d invalid highest_perf=%u (nominal=%u, lowest=%u), skipping\n", > + cpu, highest_perf, perf_caps->nominal_perf, > + perf_caps->lowest_perf); > + return; > + } > + > + if (raw_capacity[cpu] == highest_perf) > + return; > + > + pr_debug("cpu_capacity: CPU%d cpu_capacity=%u -> %u (raw)\n", > + cpu, raw_capacity[cpu], highest_perf); > + > + raw_capacity[cpu] = highest_perf; Does this actually work if highest_perf would now be the equivalent for >1024? > + per_cpu(capacity_freq_ref, cpu) = > + cppc_perf_to_khz(perf_caps, highest_perf); > + freq_inv_set_max_ratio(cpu, > + per_cpu(capacity_freq_ref, cpu) * HZ_PER_KHZ); > + > + /* Re-normalize all CPUs: capacity is relative. */ > + for_each_possible_cpu(c) > + capacity_scale = max_t(u64, capacity_scale, raw_capacity[c]); > + > + if (!capacity_scale) > + return; > + > + for_each_possible_cpu(c) { > + capacity = raw_capacity[c]; > + capacity = div64_u64(capacity << SCHED_CAPACITY_SHIFT, > + capacity_scale); > + topology_set_cpu_scale(c, capacity); > + pr_debug("cpu_capacity: CPU%d cpu_capacity=%lu\n", > + c, topology_get_cpu_scale(c)); > + } > + > + schedule_work(&update_topology_flags_work); > +} > +EXPORT_SYMBOL_GPL(topology_update_cpu_capacity); > #endif > > #ifdef CONFIG_CPU_FREQ > diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c > index 09d7745a609f..1480be537eaa 100644 > --- a/drivers/cpufreq/cppc_cpufreq.c > +++ b/drivers/cpufreq/cppc_cpufreq.c > @@ -979,6 +979,8 @@ static void cppc_cpufreq_update_limits(struct cpufreq_policy *policy) > > refresh_frequency_limits(policy); > > + topology_update_cpu_capacity(policy->cpu, caps); > + > /* > * Autonomous selection mode uses MIN/MAX performance as runtime > * hardware control bounds. Re-program them when highest_perf > diff --git a/include/linux/arch_topology.h b/include/linux/arch_topology.h > index ebd7f8935f96..9415cb6a6c2b 100644 > --- a/include/linux/arch_topology.h > +++ b/include/linux/arch_topology.h > @@ -11,6 +11,19 @@ > void topology_normalize_cpu_scale(void); > int topology_update_cpu_topology(void); > > +#ifdef CONFIG_ACPI_CPPC_LIB > +struct cppc_perf_caps; > +#ifdef CONFIG_GENERIC_ARCH_TOPOLOGY > +void topology_update_cpu_capacity(unsigned int cpu, > + struct cppc_perf_caps *perf_caps); > +#else > +static inline void > +topology_update_cpu_capacity(unsigned int cpu, struct cppc_perf_caps *perf_caps) > +{ > +} > +#endif > +#endif > + > struct device_node; > bool topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu); >