[PATCH 2/4] sparc32: wait for secondary CPUs by time, not by loop count

From: Ramin Moussavi

Date: Thu Oct 15 2026 - 05:02:11 EST


smp4m_boot_one_cpu(), smp4d_boot_one_cpu() and leon_boot_one_cpu() give
a starting CPU 10000 rounds of udelay(200) to show up in cpu_callin_map,
which is meant to be two seconds. But udelay() is only as long as
loops_per_jiffy says. Under qemu the delay loop calibration varies from
boot to boot depending on host load -- on an emulated SPARCstation 20
between 4 and 671 BogoMIPS for the same kernel and command line -- and
with a low value the wait shrinks to a few milliseconds. The boot CPU
then reports

Processor 1 is stuck.

although CPU 1 is just about to call in.

That is worse than losing a CPU. The late CPU still runs
sparc_start_secondary(), calls notify_cpu_starting(), which makes it
known to RCU, and then spins in its pre-online loop waiting for
smp_commenced_mask, which is never set for it. As it never comes
online, the grace period waits for it for good:

rcu: INFO: rcu_sched detected stalls on CPUs/tasks:
rcu: Offline CPU 1 blocking current GP.

and the boot hangs.

Wait two seconds by jiffies instead. The timer is already running at
this point; calibrate_delay() has used it.

Booting with lpj=1000 forces the short wait. On qemu's SS-20 with
-smp 4, the unpatched kernel declared all three secondary CPUs stuck
within 4 ms of starting each and hung in 4 of 4 boots; patched, it
brought up all four CPUs in 4 of 4. On leon3_generic, with two fixes
to qemu's LEON SMP support that are yet to be sent to qemu-devel, it
is the same: unpatched, all three secondary CPUs were declared stuck
in the same millisecond they were started and the boot hung with the
RCU stall above, in 2 of 2 boots; patched, all four CPUs came up in
2 of 2. sun4d is build-tested only, qemu has no sun4d machine.

Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Ramin Moussavi <ramin.moussavi@xxxxxxxxx>
---
arch/sparc/kernel/leon_smp.c | 8 +++-----
arch/sparc/kernel/sun4d_smp.c | 8 +++-----
arch/sparc/kernel/sun4m_smp.c | 8 +++-----
3 files changed, 9 insertions(+), 15 deletions(-)

diff --git a/arch/sparc/kernel/leon_smp.c b/arch/sparc/kernel/leon_smp.c
index 1ee393abc463..21e7b91cb9ca 100644
--- a/arch/sparc/kernel/leon_smp.c
+++ b/arch/sparc/kernel/leon_smp.c
@@ -180,7 +180,7 @@ void __init leon_boot_cpus(void)

int leon_boot_one_cpu(int i, struct task_struct *idle)
{
- int timeout;
+ unsigned long timeout;

current_set[i] = task_thread_info(idle);

@@ -205,11 +205,9 @@ int leon_boot_one_cpu(int i, struct task_struct *idle)
LEON_BYPASS_STORE_PA(&(leon3_irqctrl_regs->mpstatus), 1 << i);

/* wheee... it's going... */
- for (timeout = 0; timeout < 10000; timeout++) {
- if (cpu_callin_map[i])
- break;
+ timeout = jiffies + 2 * HZ;
+ while (!cpu_callin_map[i] && time_before(jiffies, timeout))
udelay(200);
- }
printk(KERN_INFO "Started CPU %d\n", (unsigned int)i);

if (!(cpu_callin_map[i])) {
diff --git a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c
index 9a62a5cf3337..9355171ff792 100644
--- a/arch/sparc/kernel/sun4d_smp.c
+++ b/arch/sparc/kernel/sun4d_smp.c
@@ -122,7 +122,7 @@ void __init smp4d_boot_cpus(void)
int smp4d_boot_one_cpu(int i, struct task_struct *idle)
{
unsigned long *entry = &sun4d_cpu_startup;
- int timeout;
+ unsigned long timeout;
int cpu_node;

cpu_find_by_instance(i, &cpu_node, NULL);
@@ -145,11 +145,9 @@ int smp4d_boot_one_cpu(int i, struct task_struct *idle)
printk(KERN_INFO "prom_startcpu returned :)\n");

/* wheee... it's going... */
- for (timeout = 0; timeout < 10000; timeout++) {
- if (cpu_callin_map[i])
- break;
+ timeout = jiffies + 2 * HZ;
+ while (!cpu_callin_map[i] && time_before(jiffies, timeout))
udelay(200);
- }

if (!(cpu_callin_map[i])) {
printk(KERN_ERR "Processor %d is stuck.\n", i);
diff --git a/arch/sparc/kernel/sun4m_smp.c b/arch/sparc/kernel/sun4m_smp.c
index 056df034e79e..4b3fc081f103 100644
--- a/arch/sparc/kernel/sun4m_smp.c
+++ b/arch/sparc/kernel/sun4m_smp.c
@@ -79,7 +79,7 @@ void __init smp4m_boot_cpus(void)
int smp4m_boot_one_cpu(int i, struct task_struct *idle)
{
unsigned long *entry = &sun4m_cpu_startup;
- int timeout;
+ unsigned long timeout;
int cpu_node;

cpu_find_by_mid(i, &cpu_node);
@@ -103,11 +103,9 @@ int smp4m_boot_one_cpu(int i, struct task_struct *idle)
prom_startcpu(cpu_node, &smp_penguin_ctable, 0, (char *)entry);

/* wheee... it's going... */
- for (timeout = 0; timeout < 10000; timeout++) {
- if (cpu_callin_map[i])
- break;
+ timeout = jiffies + 2 * HZ;
+ while (!cpu_callin_map[i] && time_before(jiffies, timeout))
udelay(200);
- }

if (!(cpu_callin_map[i])) {
printk(KERN_ERR "Processor %d is stuck.\n", i);
--
2.53.0