[PATCH] arm64: smp: Signal EOI after handling IPI_CPU_STOP*

From: Andrea della Porta

Date: Tue Sep 22 2026 - 10:26:15 EST


On kdump/kexec, the boot CPU triggers an IPI_CPU_STOP (and subsequently
an IPI_CPU_STOP_NMI if the first one does not respond) to the secondary
CPUs, and the IPI handler eventually calls the firmware to shut down
each CPU. Since the IPI is never acknowledged via EOI, the interrupt
remains in an active state when the CPU goes idle.

In a virtualized environment, if the hypervisor does not reset the
interrupt state, this causes the crashkernel (with cmdline option
maxcpus > 1) to be unable to synchronize between the boot CPU and
secondary CPUs via IPI_CALL_FUNC, leading the kernel to wait indefinitely
for the CPUs to respond. This has been observed with the Hyper-V
implementation.

Both the Linux kernel and the Hyper-V firmware appear to violate the
PSCI specification:

- The PSCI spec states that the OS kernel must migrate any interrupt away
from the CPU that is about to be shut down via CPU_OFF, which by extension
implies that no active interrupts are allowed. The kernel does not
currently do this in the kdump crash path.

- The PSCI spec also states that the PSCI firmware must reset the CPU
registers to their default values when turning on a CPU via the CPU_ON
command.

Fixing this on the kernel side has the advantage of being
hypervisor-agnostic.

Signal EOI at the end of the crash handler to prevent the subsequent
crashkernel from hanging.

Signed-off-by: Andrea della Porta <andrea.porta@xxxxxxxx>
---
arch/arm64/kernel/smp.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)

diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index a61dc3016a117..cc7f3261fe537 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -988,6 +988,27 @@ void kgdb_roundup_cpus(void)
}
#endif

+static void ipi_eoi(int ipinr)
+{
+ unsigned int cpu = smp_processor_id();
+ struct irq_desc *desc;
+ struct irq_chip *chip;
+ struct irq_data *d;
+
+ if (ipinr >= MAX_IPI)
+ return;
+
+ desc = get_ipi_desc(cpu, ipinr);
+
+ if (desc) {
+ chip = irq_desc_get_chip(desc);
+ d = irq_desc_get_irq_data(desc);
+
+ if (chip && chip->irq_eoi)
+ chip->irq_eoi(d);
+ }
+}
+
/*
* Main handler for inter-processor interrupts
*/
@@ -1009,6 +1030,7 @@ static void do_handle_IPI(int ipinr)

case IPI_CPU_STOP:
case IPI_CPU_STOP_NMI:
+ ipi_eoi(ipinr);
arm64_nmi_cpu_stop(get_irq_regs(), true);
break;

--
2.35.3