From 48bd77e5fe9ce367fb8cfbfd21a318aedf59b79f Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 30 May 2025 11:32:43 +0800 Subject: [PATCH 1/6] cpu/SMT: Move SMT prototypes into cpu_smt.h [Upstream commit 3f9169196be55590a794b52f49637561ddd1ba4f] In order to export the cpuhp_smt_control enum as part of the interface between generic and architecture code, the architecture code needs to include asm/topology.h. But that leads to circular header dependencies. So split the enum and related declarations into a separate header. [ ldufour: Reworded the commit's description ] Signed-off-by: Michael Ellerman Signed-off-by: Laurent Dufour Signed-off-by: Thomas Gleixner Tested-by: Zhang Rui Link: https://lore.kernel.org/r/20230705145143.40545-3-ldufour@linux.ibm.com --- arch/x86/include/asm/topology.h | 2 ++ include/linux/cpu.h | 25 +------------------------ include/linux/cpu_smt.h | 29 +++++++++++++++++++++++++++++ kernel/cpu.c | 1 + 4 files changed, 33 insertions(+), 24 deletions(-) create mode 100644 include/linux/cpu_smt.h diff --git a/arch/x86/include/asm/topology.h b/arch/x86/include/asm/topology.h index f3459df117aa..a6e38a29e95a 100644 --- a/arch/x86/include/asm/topology.h +++ b/arch/x86/include/asm/topology.h @@ -132,6 +132,8 @@ static inline int topology_max_smt_threads(void) return __max_smt_threads; } +#include + int topology_update_package_map(unsigned int apicid, unsigned int cpu); int topology_update_die_map(unsigned int dieid, unsigned int cpu); int topology_phys_to_logical_pkg(unsigned int pkg); diff --git a/include/linux/cpu.h b/include/linux/cpu.h index b42e9c413447..509d277c1cc9 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -18,6 +18,7 @@ #include #include #include +#include struct device; struct device_node; @@ -206,30 +207,6 @@ void cpuhp_report_idle_dead(void); static inline void cpuhp_report_idle_dead(void) { } #endif /* #ifdef CONFIG_HOTPLUG_CPU */ -enum cpuhp_smt_control { - CPU_SMT_ENABLED, - CPU_SMT_DISABLED, - CPU_SMT_FORCE_DISABLED, - CPU_SMT_NOT_SUPPORTED, - CPU_SMT_NOT_IMPLEMENTED, -}; - -#if defined(CONFIG_SMP) && defined(CONFIG_HOTPLUG_SMT) -extern enum cpuhp_smt_control cpu_smt_control; -extern void cpu_smt_disable(bool force); -extern void cpu_smt_check_topology(void); -extern bool cpu_smt_possible(void); -extern int cpuhp_smt_enable(void); -extern int cpuhp_smt_disable(enum cpuhp_smt_control ctrlval); -#else -# define cpu_smt_control (CPU_SMT_NOT_IMPLEMENTED) -static inline void cpu_smt_disable(bool force) { } -static inline void cpu_smt_check_topology(void) { } -static inline bool cpu_smt_possible(void) { return false; } -static inline int cpuhp_smt_enable(void) { return 0; } -static inline int cpuhp_smt_disable(enum cpuhp_smt_control ctrlval) { return 0; } -#endif - extern bool cpu_mitigations_off(void); extern bool cpu_mitigations_auto_nosmt(void); diff --git a/include/linux/cpu_smt.h b/include/linux/cpu_smt.h new file mode 100644 index 000000000000..722c2e306fef --- /dev/null +++ b/include/linux/cpu_smt.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_CPU_SMT_H_ +#define _LINUX_CPU_SMT_H_ + +enum cpuhp_smt_control { + CPU_SMT_ENABLED, + CPU_SMT_DISABLED, + CPU_SMT_FORCE_DISABLED, + CPU_SMT_NOT_SUPPORTED, + CPU_SMT_NOT_IMPLEMENTED, +}; + +#if defined(CONFIG_SMP) && defined(CONFIG_HOTPLUG_SMT) +extern enum cpuhp_smt_control cpu_smt_control; +extern void cpu_smt_disable(bool force); +extern void cpu_smt_check_topology(void); +extern bool cpu_smt_possible(void); +extern int cpuhp_smt_enable(void); +extern int cpuhp_smt_disable(enum cpuhp_smt_control ctrlval); +#else +# define cpu_smt_control (CPU_SMT_NOT_IMPLEMENTED) +static inline void cpu_smt_disable(bool force) { } +static inline void cpu_smt_check_topology(void) { } +static inline bool cpu_smt_possible(void) { return false; } +static inline int cpuhp_smt_enable(void) { return 0; } +static inline int cpuhp_smt_disable(enum cpuhp_smt_control ctrlval) { return 0; } +#endif + +#endif /* _LINUX_CPU_SMT_H_ */ diff --git a/kernel/cpu.c b/kernel/cpu.c index c08456af0c7f..d1b179c75c07 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -391,6 +391,7 @@ static void lockdep_release_cpus_lock(void) void __weak arch_smt_update(void) { } #ifdef CONFIG_HOTPLUG_SMT + enum cpuhp_smt_control cpu_smt_control __read_mostly = CPU_SMT_ENABLED; void __init cpu_smt_disable(bool force) -- Gitee From b92921062a3ccbd2bbd5e391f9f7a96ccd630606 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 30 May 2025 11:32:44 +0800 Subject: [PATCH 2/6] cpu/SMT: Store the current/max number of threads [Upstream commit 447ae4ac41130a7f127c2581a5e816bb0800b560] Some architectures allow partial SMT states at boot time, ie. when not all SMT threads are brought online. To support that the SMT code needs to know the maximum number of SMT threads, and also the currently configured number. The architecture code knows the max number of threads, so have the architecture code pass that value to cpu_smt_set_num_threads(). Note that although topology_max_smt_threads() exists, it is not configured early enough to be used here. As architecture, like PowerPC, allows the threads number to be set through the kernel command line, also pass that value. [ ldufour: Slightly reword the commit message ] [ ldufour: Rename cpu_smt_check_topology and add a num_threads argument ] Signed-off-by: Michael Ellerman Signed-off-by: Laurent Dufour Signed-off-by: Thomas Gleixner Tested-by: Zhang Rui Link: https://lore.kernel.org/r/20230705145143.40545-5-ldufour@linux.ibm.com --- arch/x86/kernel/cpu/bugs.c | 2 +- include/linux/cpu_smt.h | 8 ++++++-- kernel/cpu.c | 23 +++++++++++++++++++++-- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c index 1e4466ae95a4..71be1bf011be 100644 --- a/arch/x86/kernel/cpu/bugs.c +++ b/arch/x86/kernel/cpu/bugs.c @@ -123,7 +123,7 @@ void __init check_bugs(void) * identify_boot_cpu() initialized SMT support information, let the * core code know. */ - cpu_smt_check_topology(); + cpu_smt_set_num_threads(smp_num_siblings, smp_num_siblings); if (!IS_ENABLED(CONFIG_SMP)) { pr_info("CPU: "); diff --git a/include/linux/cpu_smt.h b/include/linux/cpu_smt.h index 722c2e306fef..0c1664294b57 100644 --- a/include/linux/cpu_smt.h +++ b/include/linux/cpu_smt.h @@ -12,15 +12,19 @@ enum cpuhp_smt_control { #if defined(CONFIG_SMP) && defined(CONFIG_HOTPLUG_SMT) extern enum cpuhp_smt_control cpu_smt_control; +extern unsigned int cpu_smt_num_threads; extern void cpu_smt_disable(bool force); -extern void cpu_smt_check_topology(void); +extern void cpu_smt_set_num_threads(unsigned int num_threads, + unsigned int max_threads); extern bool cpu_smt_possible(void); extern int cpuhp_smt_enable(void); extern int cpuhp_smt_disable(enum cpuhp_smt_control ctrlval); #else # define cpu_smt_control (CPU_SMT_NOT_IMPLEMENTED) +# define cpu_smt_num_threads 1 static inline void cpu_smt_disable(bool force) { } -static inline void cpu_smt_check_topology(void) { } +static inline void cpu_smt_set_num_threads(unsigned int num_threads, + unsigned int max_threads) { } static inline bool cpu_smt_possible(void) { return false; } static inline int cpuhp_smt_enable(void) { return 0; } static inline int cpuhp_smt_disable(enum cpuhp_smt_control ctrlval) { return 0; } diff --git a/kernel/cpu.c b/kernel/cpu.c index d1b179c75c07..173f69d7b88e 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -393,6 +393,8 @@ void __weak arch_smt_update(void) { } #ifdef CONFIG_HOTPLUG_SMT enum cpuhp_smt_control cpu_smt_control __read_mostly = CPU_SMT_ENABLED; +static unsigned int cpu_smt_max_threads __ro_after_init; +unsigned int cpu_smt_num_threads __read_mostly = UINT_MAX; void __init cpu_smt_disable(bool force) { @@ -406,16 +408,33 @@ void __init cpu_smt_disable(bool force) pr_info("SMT: disabled\n"); cpu_smt_control = CPU_SMT_DISABLED; } + cpu_smt_num_threads = 1; } /* * The decision whether SMT is supported can only be done after the full * CPU identification. Called from architecture code. */ -void __init cpu_smt_check_topology(void) +void __init cpu_smt_set_num_threads(unsigned int num_threads, + unsigned int max_threads) { - if (!topology_smt_supported()) + WARN_ON(!num_threads || (num_threads > max_threads)); + + if (max_threads == 1) cpu_smt_control = CPU_SMT_NOT_SUPPORTED; + + cpu_smt_max_threads = max_threads; + + /* + * If SMT has been disabled via the kernel command line or SMT is + * not supported, set cpu_smt_num_threads to 1 for consistency. + * If enabled, take the architecture requested number of threads + * to bring up into account. + */ + if (cpu_smt_control != CPU_SMT_ENABLED) + cpu_smt_num_threads = 1; + else if (num_threads < cpu_smt_num_threads) + cpu_smt_num_threads = num_threads; } static int __init smt_cmdline_disable(char *str) -- Gitee From 828a4913ce20f02fc656ec927f5560cb270c2e57 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Fri, 30 May 2025 11:32:45 +0800 Subject: [PATCH 3/6] arch_topology: Support basic SMT control for the driver The core CPU control framework supports runtime SMT control which is not yet supported by arch_topology driver and thus arch_topology based architectures. This patch implements it in the following aspects: - implement topology_is_primary_thread() to indicate the primary thread, required by the framework - architecture code can get/set the SMT thread number by topology_smt_{get, set}_num_threads() - update the SMT thread number for the framework after the topology enumerated on arm64, which is also required by the framework For disabling SMT we'll offline all the secondary threads and only leave the primary thread. Since we don't have restriction for primary thread selection, the first thread is chosen as the primary thread in this implementation. This patch only implements the basic support for SMT control, which needs to collabrate with ACPI/OF based topology building to fully enable the feature. The SMT control will be enabled unless the correct SMT thread number is set and HOTPLUG_SMT kconfig is selected. Signed-off-by: Yicong Yang Signed-off-by: Jie Liu Signed-off-by: caodongxia Signed-off-by: Xiangwei Li Signed-off-by: zhaolichang <943677312@qq.com> --- drivers/base/arch_topology.c | 40 +++++++++++++++++++++++++++++++++++ include/linux/arch_topology.h | 17 +++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index 80343cf0f9ad..cd49d3740ebb 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -468,6 +468,36 @@ const struct cpumask *cpu_clustergroup_mask(int cpu) return &cpu_topology[cpu].cluster_sibling; } +#ifdef CONFIG_HOTPLUG_SMT + +/* Maximum threads number per-Core */ +static unsigned int topology_smt_num_threads = 1; + +void __init topology_smt_set_num_threads(unsigned int num_threads) +{ + topology_smt_num_threads = num_threads; +} + +unsigned int __init topology_smt_get_num_threads(void) +{ + return topology_smt_num_threads; +} + +/* + * On SMT Hotplug the primary thread of the SMT won't be disabled. For x86 they + * seem to have a primary thread for special purpose. For other arthitectures + * like arm64 there's no such restriction for a primary thread, so make the + * first thread in the SMT as the primary thread. + */ +bool topology_is_primary_thread(unsigned int cpu) +{ + if (cpu == cpumask_first(topology_sibling_cpumask(cpu))) + return true; + + return false; +} +#endif + void update_siblings_masks(unsigned int cpuid) { struct cpu_topology *cpu_topo, *cpuid_topo = &cpu_topology[cpuid]; @@ -568,6 +598,16 @@ void __init init_cpu_topology(void) reset_cpu_topology(); else if (of_have_populated_dt() && parse_dt_topology()) reset_cpu_topology(); + + /* + * By this stage we get to know whether we support SMT or not, update + * the information for the core. We don't support + * CONFIG_SMT_NUM_THREADS_DYNAMIC so make the + * max_threads == num_threads. + */ + cpu_smt_set_num_threads(topology_smt_get_num_threads(), + topology_smt_get_num_threads()); + } void store_cpu_topology(unsigned int cpuid) diff --git a/include/linux/arch_topology.h b/include/linux/arch_topology.h index 9d7a80debe06..d4bd67d01d0f 100644 --- a/include/linux/arch_topology.h +++ b/include/linux/arch_topology.h @@ -62,6 +62,23 @@ const struct cpumask *cpu_clustergroup_mask(int cpu); void update_siblings_masks(unsigned int cpu); void remove_cpu_topology(unsigned int cpuid); void reset_cpu_topology(void); + +#ifdef CONFIG_HOTPLUG_SMT +bool topology_is_primary_thread(unsigned int cpu); +void topology_smt_set_num_threads(unsigned int num_threads); +unsigned int topology_smt_get_num_threads(void); +#else +static inline bool topology_is_primary_thread(unsigned int cpu) +{ + return false; +} +static inline void topology_smt_set_num_threads(unsigned int num_threads) { } +static inline unsigned int topology_smt_get_num_threads(void) +{ + return 1; +} +#endif + #endif #endif /* _LINUX_ARCH_TOPOLOGY_H_ */ -- Gitee From 7e40475aba0e0cb55b648e6583ea817fda247aa8 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Fri, 30 May 2025 11:32:46 +0800 Subject: [PATCH 4/6] arch_topology: Support SMT control for OF based system On building the topology from the devicetree, we've already gotten the SMT thread number of each core. Update the largest SMT thread number to enable the SMT control. Signed-off-by: Yicong Yang Signed-off-by: Jie Liu Signed-off-by: caodongxia Signed-off-by: Xiangwei Li Signed-off-by: zhaolichang <943677312@qq.com> --- drivers/base/arch_topology.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index cd49d3740ebb..cd0f09443f97 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -305,6 +305,13 @@ static int __init parse_core(struct device_node *core, int package_id, i++; } while (t); + /* + * We've already gotten threads number in this core, update the SMT + * threads number when necessary. + */ + if (i > topology_smt_get_num_threads()) + topology_smt_set_num_threads(i); + cpu = get_cpu_for_node(core); if (cpu >= 0) { if (!leaf) { -- Gitee From ac4bf7647c72f7525b34e61139d6bd248bd350ae Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Fri, 30 May 2025 11:32:47 +0800 Subject: [PATCH 5/6] arm64: topology: Support SMT control on ACPI based system For ACPI we'll build the topology from PPTT and we cannot directly get the SMT number of each core. Instead using a temporary xarray to record the SMT number of each core when building the topology and we can know the largest SMT number in the system. Then we can notify the arch_topology for supporting SMT control. Signed-off-by: Yicong Yang Signed-off-by: Jie Liu Signed-off-by: caodongxia Signed-off-by: Xiangwei Li Signed-off-by: zhaolichang <943677312@qq.com> --- arch/arm64/kernel/topology.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c index 4ecf361cb562..b3a4e1d01ae8 100644 --- a/arch/arm64/kernel/topology.c +++ b/arch/arm64/kernel/topology.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -42,11 +43,16 @@ static bool __init acpi_cpu_is_threaded(int cpu) */ int __init parse_acpi_topology(void) { + int thread_num, max_smt_thread_num = 1; + struct xarray core_threads; int cpu, topology_id; + void *entry; if (acpi_disabled) return 0; + xa_init(&core_threads); + for_each_possible_cpu(cpu) { int i, cache_id; @@ -58,6 +64,20 @@ int __init parse_acpi_topology(void) cpu_topology[cpu].thread_id = topology_id; topology_id = find_acpi_cpu_topology(cpu, 1); cpu_topology[cpu].core_id = topology_id; + + entry = xa_load(&core_threads, topology_id); + if (!entry) { + xa_store(&core_threads, topology_id, + xa_mk_value(1), GFP_KERNEL); + } else { + thread_num = xa_to_value(entry); + thread_num++; + xa_store(&core_threads, topology_id, + xa_mk_value(thread_num), GFP_KERNEL); + + if (thread_num > max_smt_thread_num) + max_smt_thread_num = thread_num; + } } else { cpu_topology[cpu].thread_id = -1; cpu_topology[cpu].core_id = topology_id; @@ -80,6 +100,9 @@ int __init parse_acpi_topology(void) } } + topology_smt_set_num_threads(max_smt_thread_num); + + xa_destroy(&core_threads); return 0; } #endif -- Gitee From 768ae2c1dda9fb4b4aae4edac5f0a8aaad263816 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Fri, 30 May 2025 11:32:48 +0800 Subject: [PATCH 6/6] arm64: Kconfig: Enable HOTPLUG_SMT [Upstream commit eed4583bcf9a60f8d6dd3a3c7c94dea28134b1eb] Enable HOTPLUG_SMT for SMT control. Reviewed-by: Jonathan Cameron Reviewed-by: Pierre Gondois Reviewed-by: Dietmar Eggemann Signed-off-by: Yicong Yang Reviewed-by: Sudeep Holla Link: https://lore.kernel.org/r/20250311075143.61078-5-yangyicong@huawei.com Signed-off-by: Catalin Marinas --- arch/arm64/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 6a7d7bf4b35a..b3eb384c4cd8 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -175,6 +175,7 @@ config ARM64 select HAVE_KRETPROBES select HAVE_GENERIC_VDSO select IOMMU_DMA if IOMMU_SUPPORT + select HOTPLUG_SMT if (SMP && HOTPLUG_CPU) select IRQ_DOMAIN select IRQ_FORCED_THREADING select MODULES_USE_ELF_RELA -- Gitee