* [PATCH] arm64: smp: distinguish secondary CPUs that hang after reaching head.S
@ 2026-07-22 11:30 Naman Jain
0 siblings, 0 replies; only message in thread
From: Naman Jain @ 2026-07-22 11:30 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon
Cc: linux-arm-kernel, linux-kernel, Marc Zyngier, Thomas Huth,
Fuad Tabba, Jinjie Ruan, Thomas Gleixner, Pengjie Zhang,
mrigendrachaubey, Saurabh Sengar
When a secondary CPU fails to come online, __cpu_up() falls back to
__early_cpu_boot_status, but boot status 0x0 is ambiguous: it cannot
distinguish a CPU that never executed head.S (firmware/hypervisor never
dispatched it, so it never ran a single instruction) from one that
entered head.S, started executing, and then got stuck somewhere in kernel
bring-up. Add a change to let us tell those two cases apart, which
narrows down where to look when a CPU goes missing during boot.
Write a sentinel (CPU_BOOT_ASM_ENTERED, 0x10) as the first thing in
secondary_entry so the two cases can be told apart:
0x0 - CPU never ran head.S
0x10 - CPU entered head.S but did not come online
__cpu_up() resets the word before releasing each secondary, so a stale
value is not misattributed. Nothing clears the marker on the success
path. Reason being, the CPU instead sets CPU_BOOT_SUCCESS in
secondary_data.status, which __cpu_up() checks first, so
__early_cpu_boot_status is only read on failure. A stall before
CPU_BOOT_SUCCESS - including in secondary_start_kernel() - is therefore
reported as 0x10. The sentinel sits in the low byte and sets no
CPU_STUCK_REASON_* bits, so existing status decoding is unchanged.
Limitations: best-effort only. The status variable is global with no CPU
identity, and spin-table secondaries boot via secondary_holding_pen and
never write it, so 0x0 is a strong hint rather than a guarantee.
Signed-off-by: Naman Jain <namjain@linux.microsoft.com>
---
arch/arm64/include/asm/smp.h | 10 ++++++++++
arch/arm64/kernel/head.S | 24 +++++++++++++++++++++++-
arch/arm64/kernel/smp.c | 21 +++++++++++++++++++++
3 files changed, 54 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/include/asm/smp.h b/arch/arm64/include/asm/smp.h
index 10ea4f5430690..ca330cd1b0615 100644
--- a/arch/arm64/include/asm/smp.h
+++ b/arch/arm64/include/asm/smp.h
@@ -20,6 +20,16 @@
/* Fatal system error detected by secondary CPU, crash the system */
#define CPU_PANIC_KERNEL (3)
+/*
+ * Best-effort marker written early in secondary_entry (PSCI/hotplug
+ * path) so __cpu_up() can tell whether a stuck CPU ever ran head.S.
+ * Not authoritative: spin-table secondaries use secondary_holding_pen
+ * and never write it, and the word has no CPU identity. Value must sit
+ * in the low byte, stay out of {0,1,2,3}, and set no CPU_STUCK_REASON_*
+ * bits.
+ */
+#define CPU_BOOT_ASM_ENTERED (0x10)
+
#define CPU_STUCK_REASON_52_BIT_VA (UL(1) << CPU_STUCK_REASON_SHIFT)
#define CPU_STUCK_REASON_NO_GRAN (UL(2) << CPU_STUCK_REASON_SHIFT)
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
index 87a822e5c4ca8..8ac3c49b4daf7 100644
--- a/arch/arm64/kernel/head.S
+++ b/arch/arm64/kernel/head.S
@@ -353,6 +353,22 @@ SYM_FUNC_END(secondary_holding_pen)
* be used where CPUs are brought online dynamically by the kernel.
*/
SYM_FUNC_START(secondary_entry)
+ /*
+ * Record that this CPU reached head.S, before init_kernel_el()
+ * sets SCTLR_ELx.EE. A single-byte store to the reader's low
+ * byte (__cpu_up() zeroed the word) is endian-agnostic, so a
+ * big-endian kernel still reads 0x10. Flush it like
+ * update_early_cpu_boot_status.
+ */
+ adr_l x2, __early_cpu_boot_status
+ mov w1, #CPU_BOOT_ASM_ENTERED
+#ifdef CONFIG_CPU_BIG_ENDIAN
+ strb w1, [x2, #7] // low byte at top address
+#else
+ strb w1, [x2]
+#endif
+ dmb sy
+ dc ivac, x2
mov x0, xzr
bl init_kernel_el // w0=cpu_boot_mode
b secondary_startup
@@ -386,7 +402,6 @@ SYM_FUNC_START_LOCAL(__secondary_switched)
mov x0, x20
bl finalise_el2
- str_l xzr, __early_cpu_boot_status, x3
adr_l x5, vectors
msr vbar_el1, x5
isb
@@ -395,6 +410,13 @@ SYM_FUNC_START_LOCAL(__secondary_switched)
ldr x2, [x0, #CPU_BOOT_TASK]
cbz x2, __secondary_too_slow
+ /*
+ * Don't clear the marker here; nothing on the secondary clears
+ * it. Once the CPU sets CPU_BOOT_SUCCESS in
+ * secondary_data.status, __cpu_up() stops reading
+ * __early_cpu_boot_status (and resets it before the next
+ * release), so a stall before that still reports 0x10.
+ */
init_cpu_task x2, x1, x3
#ifdef CONFIG_ARM64_PTR_AUTH
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index cdcdd160e5b69..662205ae5a7a6 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -120,6 +120,17 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)
secondary_data.task = idle;
update_cpu_boot_status(CPU_MMU_OFF);
+ /*
+ * Reset the marker before releasing this secondary: head.S
+ * never clears it, so without this a CPU that never reaches
+ * head.S would inherit a stale 0x10 from a previous CPU. Flush
+ * it so the MMU-off secondary sees the reset.
+ */
+ WRITE_ONCE(__early_cpu_boot_status, 0);
+ dcache_clean_inval_poc((unsigned long)&__early_cpu_boot_status,
+ (unsigned long)&__early_cpu_boot_status +
+ sizeof(__early_cpu_boot_status));
+
/* Now bring the CPU into our world */
ret = boot_secondary(cpu, idle);
if (ret) {
@@ -145,10 +156,20 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)
switch (status & CPU_BOOT_STATUS_MASK) {
default:
+ /*
+ * 0x0 usually means the CPU never ran head.S, but
+ * treat it as a hint: spin-table secondaries never
+ * write the marker and the word has no CPU identity.
+ */
pr_err("CPU%u: failed in unknown state : 0x%lx\n",
cpu, status);
cpus_stuck_in_kernel++;
break;
+ case CPU_BOOT_ASM_ENTERED:
+ pr_err("CPU%u: entered head.S but not online : 0x%lx\n",
+ cpu, status);
+ cpus_stuck_in_kernel++;
+ break;
case CPU_KILL_ME:
if (!op_cpu_kill(cpu)) {
pr_crit("CPU%u: died during early boot\n", cpu);
base-commit: 290aaf24a551d5a0dce037e3fab30820f9113a10
--
2.43.0
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-07-22 11:31 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-22 11:30 [PATCH] arm64: smp: distinguish secondary CPUs that hang after reaching head.S Naman Jain
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox