ARM64 Hyper-V guests currently lack the VMBus teardown that x86 performs
during kexec via hv_machine_shutdown(). Without this, kexec fails because:

1. The kexec'd kernel's vmbus_connect() hangs because the hypervisor
   still has the old VMBus connection active.
2. Stale SynIC state (message pending flags, event flags) causes
   message delivery failures or NULL pointer dereferences in the
   kexec'd kernel.

The VMBus driver already provides a kexec cleanup callback via the
existing hv_setup_kexec_handler() / hv_remove_kexec_handler() API
(drivers/hv/vmbus_drv.c). These call into arch-specific overrides;
on x86, the override is in mshyperv.c, while ARM64 currently uses
the __weak no-op stubs from hv_common.c.

This patch provides the ARM64 override of hv_setup_kexec_handler()
and hv_remove_kexec_handler(), wiring the registered hv_kexec_handler
to the new arm64_pre_smp_shutdown_hook so that it is invoked during
machine_shutdown(). This matches x86's ordering: the handler runs
AFTER device_shutdown() (so PCI drivers can clean up interrupt
mappings via PCI_DELETE_INTERRUPT_MESSAGE before VMBus UNLOAD
force-closes channels) and AFTER cpu_hotplug_enable() (so
cpuhp_remove_state() can disable SynIC on all CPUs).

hv_kexec_handler() (registered by vmbus_drv.c via
hv_setup_kexec_handler()) performs:
  - vmbus_initiate_unload(false): sends CHANNELMSG_UNLOAD to host
  - cpuhp_remove_state(): disables SynIC (SIMP, SIEFP, SINT) on all
    CPUs, ensuring the kexec'd kernel starts with clean SynIC state

Signed-off-by: Shradha Gupta <[email protected]>
---
 arch/arm64/hyperv/mshyperv.c | 37 ++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/arch/arm64/hyperv/mshyperv.c b/arch/arm64/hyperv/mshyperv.c
index 4fdc26ade1d7..4176175060d3 100644
--- a/arch/arm64/hyperv/mshyperv.c
+++ b/arch/arm64/hyperv/mshyperv.c
@@ -15,10 +15,47 @@
 #include <linux/errno.h>
 #include <linux/version.h>
 #include <linux/cpuhotplug.h>
+#include <linux/kexec.h>
 #include <asm/mshyperv.h>
+#include <asm/system_misc.h>
 
 static bool hyperv_initialized;
 
+/*
+ * Kexec/shutdown handler for ARM64 Hyper-V guests.
+ *
+ * On x86, hv_machine_shutdown() overrides machine_ops.shutdown and
+ * runs after device_shutdown() and cpu_hotplug_enable(). ARM64 uses
+ * the arm64_pre_smp_shutdown_hook to achieve the same ordering.
+ *
+ * hv_kexec_handler() (set by vmbus_drv.c) performs:
+ *   1. vmbus_initiate_unload(false) - sends CHANNELMSG_UNLOAD
+ *   2. cpuhp_remove_state(hyperv_cpuhp_online) - disables SynIC per CPU
+ *
+ * By running after device_shutdown(), PCI drivers (NVMe, MANA) can
+ * send PCI_DELETE_INTERRUPT_MESSAGE and clean up MMIO/interrupt
+ * mappings before VMBus channels are force-closed by UNLOAD.
+ */
+static void (*hv_kexec_handler)(void);
+
+static void hv_machine_shutdown(void)
+{
+       if (kexec_in_progress && hv_kexec_handler)
+               hv_kexec_handler();
+}
+
+void hv_setup_kexec_handler(void (*handler)(void))
+{
+       hv_kexec_handler = handler;
+       arm64_pre_smp_shutdown_hook = hv_machine_shutdown;
+}
+
+void hv_remove_kexec_handler(void)
+{
+       arm64_pre_smp_shutdown_hook = NULL;
+       hv_kexec_handler = NULL;
+}
+
 int hv_get_hypervisor_version(union hv_hypervisor_version_info *info)
 {
        hv_get_vpreg_128(HV_REGISTER_HYPERVISOR_VERSION,
-- 
2.43.0


Reply via email to