[PATCH RFC v1 1/8] cpu/hotplug: Allow architecture-specific primary CPU bringup

From: Chang S. Bae

Date: Fri Sep 11 2026 - 20:36:46 EST


Parallel CPU bringup currently proceeds in two phases: first for the SMT
primary threads and then second for the remaining secondary threads. This
ordering avoids race conditions, specifically for x86 microcode loading.

Upcoming x86 changes expand the microcode loading scope beyond a single
core to package scope or even system-wide updates. Introduce architecture
hooks that allow customizing which CPUs participate in the first phase:

* arch_cpuhp_primary_aware() - indicates whether the architecture
provides a custom primary CPU selection scheme, or not.

* arch_cpuhp_get_primary_cpus() - returns a CPU mask for the first
bringup iteration.

Signed-off-by: Chang S. Bae <chang.seok.bae@xxxxxxxxx>
---
arch/Kconfig | 4 ++++
include/linux/cpu.h | 9 +++++++++
kernel/cpu.c | 8 ++++++--
3 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/arch/Kconfig b/arch/Kconfig
index 45c657772362..f38d80d583bf 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -102,6 +102,10 @@ config HOTPLUG_PARALLEL
bool
select HOTPLUG_SPLIT_STARTUP

+config HOTPLUG_PARALLEL_ARCH_PRIMARY
+ bool
+ depends on HOTPLUG_PARALLEL
+
config GENERIC_IRQ_ENTRY
bool

diff --git a/include/linux/cpu.h b/include/linux/cpu.h
index 9b6b0d87fdb0..11e375d00df7 100644
--- a/include/linux/cpu.h
+++ b/include/linux/cpu.h
@@ -19,6 +19,7 @@
#include <linux/cpuhotplug.h>
#include <linux/cpuhplock.h>
#include <linux/cpu_smt.h>
+#include <linux/cpumask.h>

struct device;
struct device_node;
@@ -233,4 +234,12 @@ int arch_prctl_get_branch_landing_pad_state(struct task_struct *t, unsigned long
int arch_prctl_set_branch_landing_pad_state(struct task_struct *t, unsigned long state);
int arch_prctl_lock_branch_landing_pad_state(struct task_struct *t);

+#ifdef CONFIG_HOTPLUG_PARALLEL_ARCH_PRIMARY
+bool __init arch_cpuhp_primary_aware(void);
+const struct cpumask *__init arch_cpuhp_get_primary_cpus(void);
+#else
+static inline bool arch_cpuhp_primary_aware(void) { return false; }
+static inline const struct cpumask *arch_cpuhp_get_primary_cpus(void) { return cpu_none_mask; }
+#endif
+
#endif /* _LINUX_CPU_H_ */
diff --git a/kernel/cpu.c b/kernel/cpu.c
index b3c8553d7bd6..5225f91c4d8c 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -1837,15 +1837,19 @@ static bool __init cpuhp_bringup_cpus_parallel(unsigned int ncpus)
if (!__cpuhp_parallel_bringup)
return false;

- if (cpuhp_smt_aware()) {
- const struct cpumask *pmask = cpuhp_get_primary_thread_mask();
+ if (arch_cpuhp_primary_aware() || cpuhp_smt_aware()) {
static struct cpumask tmp_mask __initdata;
+ const struct cpumask *pmask;

/*
* X86 requires to prevent that SMT siblings stopped while
* the primary thread does a microcode update for various
* reasons. Bring the primary threads up first.
*/
+ pmask = arch_cpuhp_primary_aware() ?
+ arch_cpuhp_get_primary_cpus() :
+ cpuhp_get_primary_thread_mask();
+
cpumask_and(&tmp_mask, mask, pmask);
cpuhp_bringup_mask(&tmp_mask, ncpus, CPUHP_BP_KICK_AP);
cpuhp_bringup_mask(&tmp_mask, ncpus, CPUHP_ONLINE);
--
2.53.0