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);
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.