[PATCH v2 3/4] arm64: smp: Defer RCU reporting until after local CPU capability checks
Jinjie Ruan
ruanjinjie at huawei.com
Thu Jun 18 02:24:43 PDT 2026
To support HOTPLUG_PARALLEL on arm64, and to prevent a potential deadlock
on the control CPU, check_local_cpu_capabilities() must be executed
before cpuhp_ap_sync_alive(). This ensures that if an early capability
mismatch occurs and the AP invokes cpu_die_early(), the control CPU
can detect the boot timeout and proceed, rather than hanging
indefinitely.
Furthermore, under parallel bringup, cpuhp_ap_sync_alive() must be called
before rcutree_report_cpu_starting(). This sequence prevents a false
RCU CPU Stall Warning caused by the prolonged spin-waiting/busy-waiting
required during the AP synchronization process.
GICv3: CPU1: using allocated LPI pending table @0x0000000104160000
CPU1: Booted secondary processor 0x0000000001 [0x410fd082]
rcu: INFO: rcu_preempt detected stalls on CPUs/tasks:
rcu: 2-O..!: (2 GPs behind) idle=0004/1/0x4000000000000000 softirq=0/0 fqs=2625
rcu: 3-O..!: (2 GPs behind) idle=0004/1/0x4000000000000000 softirq=0/0 fqs=2625
rcu: (detected by 0, t=5252 jiffies, g=-1187, q=1 ncpus=16)
rcu: Offline CPU 2 blocking current GP.
rcu: Offline CPU 3 blocking current GP.
To avoid suspicious RCU usage, commit ce3d31ad3cac ("arm64/smp: Move
rcu_cpu_starting() earlier") move rcutree_report_cpu_starting() earlier
which is before check_local_cpu_capabilities().
But For parallel bringup, the order should be as following:
secondary_start_kernel()
-> check_local_cpu_capabilities()
-> cpu_die_early()
-> cpuhp_ap_sync_alive()
-> rcutree_report_cpu_starting()
And this required order forces standard printk/pr_* statements inside
check_local_cpu_capabilities() to execute while the secondary CPU is still
marked as offline to RCU, triggering a lockdep "suspicious RCU usage" splat
due to console semaphore operations.
So converting early capability logging and failure paths to printk_deferred().
This pushes the logs into the lockless ringbuffer without triggering console
locks or RCU validation on the offline CPU, resolving the lockdep splat while
preserving critical error messages and the strictly required initialization
order.
Signed-off-by: Jinjie Ruan <ruanjinjie at huawei.com>
---
arch/arm64/kernel/cpufeature.c | 20 ++++++++++----------
arch/arm64/kernel/smp.c | 4 ++--
arch/arm64/mm/context.c | 2 +-
3 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 0552202702bf..a5e0bc4d383b 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -3546,7 +3546,7 @@ static void update_cpu_capabilities(u16 scope_mask)
* system capabilities are finalised.
*/
if (!match_all && caps->desc && !caps->cpus)
- pr_info("detected: %s\n", caps->desc);
+ printk_deferred(KERN_INFO "detected: %s\n", caps->desc);
__set_bit(caps->capability, system_cpucaps);
@@ -3669,7 +3669,7 @@ static void verify_local_cpu_caps(u16 scope_mask)
}
if (i < ARM64_NCAPS) {
- pr_crit("CPU%d: Detected conflict for capability %d (%s), System: %d, CPU: %d\n",
+ printk_deferred(KERN_CRIT "CPU%d: Detected conflict for capability %d (%s), System: %d, CPU: %d\n",
smp_processor_id(), caps->capability,
caps->desc, system_has_cap, cpu_has_cap);
@@ -3697,7 +3697,7 @@ __verify_local_elf_hwcaps(const struct arm64_cpu_capabilities *caps)
for (; caps->matches; caps++)
if (cpus_have_elf_hwcap(caps) && !caps->matches(caps, SCOPE_LOCAL_CPU)) {
- pr_crit("CPU%d: missing HWCAP: %s\n",
+ printk_deferred(KERN_CRIT "CPU%d: missing HWCAP: %s\n",
smp_processor_id(), caps->desc);
cpu_die_early();
}
@@ -3716,7 +3716,7 @@ static void verify_sve_features(void)
unsigned long cpacr = cpacr_save_enable_kernel_sve();
if (vec_verify_vq_map(ARM64_VEC_SVE)) {
- pr_crit("CPU%d: SVE: vector length support mismatch\n",
+ printk_deferred(KERN_CRIT "CPU%d: SVE: vector length support mismatch\n",
smp_processor_id());
cpu_die_early();
}
@@ -3729,7 +3729,7 @@ static void verify_sme_features(void)
unsigned long cpacr = cpacr_save_enable_kernel_sme();
if (vec_verify_vq_map(ARM64_VEC_SME)) {
- pr_crit("CPU%d: SME: vector length support mismatch\n",
+ printk_deferred(KERN_CRIT "CPU%d: SME: vector length support mismatch\n",
smp_processor_id());
cpu_die_early();
}
@@ -3754,7 +3754,7 @@ static void verify_hyp_capabilities(void)
safe_vmid_bits = get_vmid_bits(safe_mmfr1);
vmid_bits = get_vmid_bits(mmfr1);
if (vmid_bits < safe_vmid_bits) {
- pr_crit("CPU%d: VMID width mismatch\n", smp_processor_id());
+ printk_deferred(KERN_CRIT "CPU%d: VMID width mismatch\n", smp_processor_id());
cpu_die_early();
}
@@ -3763,7 +3763,7 @@ static void verify_hyp_capabilities(void)
ID_AA64MMFR0_EL1_PARANGE_SHIFT);
ipa_max = id_aa64mmfr0_parange_to_phys_shift(parange);
if (ipa_max < get_kvm_ipa_limit()) {
- pr_crit("CPU%d: IPA range mismatch\n", smp_processor_id());
+ printk_deferred(KERN_CRIT "CPU%d: IPA range mismatch\n", smp_processor_id());
cpu_die_early();
}
}
@@ -3776,7 +3776,7 @@ static void verify_mpam_capabilities(void)
if (FIELD_GET(ID_AA64PFR0_EL1_MPAM_MASK, cpu_idr) !=
FIELD_GET(ID_AA64PFR0_EL1_MPAM_MASK, sys_idr)) {
- pr_crit("CPU%d: MPAM version mismatch\n", smp_processor_id());
+ printk_deferred(KERN_CRIT "CPU%d: MPAM version mismatch\n", smp_processor_id());
cpu_die_early();
}
@@ -3784,7 +3784,7 @@ static void verify_mpam_capabilities(void)
sys_idr = read_sanitised_ftr_reg(SYS_MPAMIDR_EL1);
if (FIELD_GET(MPAMIDR_EL1_HAS_HCR, cpu_idr) !=
FIELD_GET(MPAMIDR_EL1_HAS_HCR, sys_idr)) {
- pr_crit("CPU%d: Missing MPAM HCR\n", smp_processor_id());
+ printk_deferred(KERN_CRIT "CPU%d: Missing MPAM HCR\n", smp_processor_id());
cpu_die_early();
}
@@ -3793,7 +3793,7 @@ static void verify_mpam_capabilities(void)
sys_partid_max = FIELD_GET(MPAMIDR_EL1_PARTID_MAX, sys_idr);
sys_pmg_max = FIELD_GET(MPAMIDR_EL1_PMG_MAX, sys_idr);
if (cpu_partid_max < sys_partid_max || cpu_pmg_max < sys_pmg_max) {
- pr_crit("CPU%d: MPAM PARTID/PMG max values are mismatched\n", smp_processor_id());
+ printk_deferred(KERN_CRIT "CPU%d: MPAM PARTID/PMG max values are mismatched\n", smp_processor_id());
cpu_die_early();
}
}
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 6bc90ee4820a..52edabc13d51 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -215,7 +215,6 @@ asmlinkage notrace void secondary_start_kernel(void)
if (system_uses_irq_prio_masking())
init_gic_priority_masking();
- rcutree_report_cpu_starting(cpu);
trace_hardirqs_off();
/*
@@ -224,6 +223,7 @@ asmlinkage notrace void secondary_start_kernel(void)
* fail to come online.
*/
check_local_cpu_capabilities();
+ rcutree_report_cpu_starting(cpu);
ops = get_cpu_ops(cpu);
if (ops->cpu_postboot)
@@ -404,7 +404,7 @@ void __noreturn cpu_die_early(void)
{
int cpu = smp_processor_id();
- pr_crit("CPU%d: will not boot\n", cpu);
+ printk_deferred(KERN_CRIT "CPU%d: will not boot\n", cpu);
/* Mark this CPU absent */
set_cpu_present(cpu, 0);
diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c
index 6b8a3245f393..9b5ab56aad5a 100644
--- a/arch/arm64/mm/context.c
+++ b/arch/arm64/mm/context.c
@@ -70,7 +70,7 @@ void verify_cpu_asid_bits(void)
* We cannot decrease the ASID size at runtime, so panic if we support
* fewer ASID bits than the boot CPU.
*/
- pr_crit("CPU%d: smaller ASID size(%u) than boot CPU (%u)\n",
+ printk_deferred(KERN_CRIT "CPU%d: smaller ASID size(%u) than boot CPU (%u)\n",
smp_processor_id(), asid, asid_bits);
cpu_panic_kernel(smp_processor_id());
}
--
2.34.1
More information about the linux-riscv
mailing list