[PATCH v4 2/2] Drivers: hv: Run VMBus kexec cleanup from syscore shutdown
Mukesh Pilaniya
mpilaniy at redhat.com
Mon Oct 26 03:22:27 PDT 2026
On 09/10/26 3:52 pm, Shradha Gupta wrote:
> ARM64 Hyper-V guests do not run the VMBus kexec cleanup before
> secondary CPUs are stopped. Consequently, the old VMBus connection
> and SynIC state remain active when the kexec kernel starts.
>
> Run the VMBus kexec handler from the existing VMBus syscore shutdown
> callback. syscore_shutdown() runs after device shutdown and before
> machine shutdown, while all CPUs needed by the SynIC CPU hotplug
> teardown are still available. Guard the callback with
> kexec_is_in_progress() so normal restart, halt, and poweroff paths are
> unchanged.
>
> Use the same path on x86 and remove the architecture-specific handler
> pointer and the weak setup/remove interfaces. VMBus teardown now
> precedes hv_stimer_global_cleanup() on x86.
>
> The new x86 ordering relies on commit be0cfab740e5 ("clocksource:
> hyper-v: Remove support for stimer interrupts in message mode"), which
> removed the synthetic timer's dependency on VMBus message delivery.
> Older kernels that retain message-mode stimer interrupts must preserve
> the existing x86 ordering and therefore require an ARM64-specific
> backport.
>
> Document the VMBus cleanup path and its position in shutdown ordering.
>
> Fixes: 9bbb888824e3 ("arm64: hyperv: Initialize hypervisor on boot")
> Link: https://lore.kernel.org/all/20260814093133.3191250-1-shradhagupta@linux.microsoft.com/
> Suggested-by: Catalin Marinas <catalin.marinas at arm.com>
> Suggested-by: Michael Kelley <mhklinux at outlook.com>
> Signed-off-by: Shradha Gupta <shradhagupta at linux.microsoft.com>
> Reviewed-by: Michael Kelley <mhklinux at outlook.com>
> Reviewed-by: Bradley Morgan <brads at mainlining.org>
> Reviewed-by: Naman Jain <namjain at linux.microsoft.com>
> ---
> Documentation/virt/hyperv/overview.rst | 12 +++++++-----
> arch/x86/kernel/cpu/mshyperv.c | 27 ++++++---------------------
> drivers/hv/hv_common.c | 10 ----------
> drivers/hv/vmbus_drv.c | 22 ++++++++++++++--------
> include/asm-generic/mshyperv.h | 2 --
> 5 files changed, 27 insertions(+), 46 deletions(-)
>
> diff --git a/Documentation/virt/hyperv/overview.rst b/Documentation/virt/hyperv/overview.rst
> index 77408a89d1a4..34a53ba7a122 100644
> --- a/Documentation/virt/hyperv/overview.rst
> +++ b/Documentation/virt/hyperv/overview.rst
> @@ -114,11 +114,13 @@ a shared page or remove an overlay page after the new kernel is
> using the page for a different purpose, corrupting the new kernel.
> Hyper-V does not provide a single "set everything" operation to
> guest VMs, so Linux code must individually revoke all sharing before
> -doing kexec. See hv_kexec_handler() and hv_crash_handler(). But
> -the crash/panic path still has holes in cleanup because some shared
> -pages are set using per-CPU synthetic registers and there's no
> -mechanism to revoke the shared pages for CPUs other than the CPU
> -running the panic path.
> +doing kexec. For a normal kexec reboot, the VMBus driver calls
> +hv_kexec_handler() from its syscore shutdown callback, after device
> +shutdown and before the architecture-specific machine shutdown. The
> +crash/panic path uses hv_crash_handler(), but still has holes in
> +cleanup because some shared pages are set using per-CPU synthetic
> +registers and there is no mechanism to revoke the shared pages for
> +CPUs other than the CPU running the panic path.
>
> CPU Management
> --------------
> diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c
> index b4af7c0a70ac..fec1c37dafbf 100644
> --- a/arch/x86/kernel/cpu/mshyperv.c
> +++ b/arch/x86/kernel/cpu/mshyperv.c
> @@ -148,7 +148,6 @@ EXPORT_SYMBOL_GPL(hv_set_msr);
> static void (*mshv_handler)(void);
> static void (*vmbus_handler)(void);
> static void (*hv_stimer0_handler)(void);
> -static void (*hv_kexec_handler)(void);
> static void (*hv_crash_handler)(struct pt_regs *regs);
>
> DEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_callback)
> @@ -215,16 +214,6 @@ void hv_remove_stimer0_handler(void)
> hv_stimer0_handler = NULL;
> }
>
> -void hv_setup_kexec_handler(void (*handler)(void))
> -{
> - hv_kexec_handler = handler;
> -}
> -
> -void hv_remove_kexec_handler(void)
> -{
> - hv_kexec_handler = NULL;
> -}
> -
> void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs))
> {
> hv_crash_handler = handler;
> @@ -240,17 +229,13 @@ static void hv_machine_shutdown(void)
> {
> if (kexec_in_progress) {
> hv_stimer_global_cleanup();
> -
> - if (hv_kexec_handler)
> - hv_kexec_handler();
> - }
> -
> - /*
> - * Call hv_cpu_die() on all the CPUs, otherwise later the hypervisor
> - * corrupts the old VP Assist Pages and can crash the kexec kernel.
> - */
> - if (kexec_in_progress)
> + /*
> + * Call hv_cpu_die() on all the CPUs, otherwise later the
> + * hypervisor corrupts the old VP Assist Pages and can crash
> + * the kexec kernel.
> + */
> cpuhp_remove_state(CPUHP_AP_HYPERV_ONLINE);
> + }
>
After this patch, the x86 kexec path ends up with two
cpuhp_remove_state() calls, one inside hv_kexec_handler() (now fired
from syscore shutdown) on hyperv_cpuhp_online, and one in
hv_machine_shutdown() on CPUHP_AP_HYPERV_ONLINE.
Could you confirm these refer to distinct CPUHP slots? If they resolve
to the same slot, we'd be calling cpuhp_remove_state() twice on it,
which would be a bug.
> /* The function calls stop_other_cpus(). */
> native_machine_shutdown();
> diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c
> index 31256cb22b39..bb290a5eaf1d 100644
> --- a/drivers/hv/hv_common.c
> +++ b/drivers/hv/hv_common.c
> @@ -681,16 +681,6 @@ void __weak hv_setup_mshv_handler(void (*handler)(void))
> }
> EXPORT_SYMBOL_GPL(hv_setup_mshv_handler);
>
> -void __weak hv_setup_kexec_handler(void (*handler)(void))
> -{
> -}
> -EXPORT_SYMBOL_GPL(hv_setup_kexec_handler);
> -
> -void __weak hv_remove_kexec_handler(void)
> -{
> -}
> -EXPORT_SYMBOL_GPL(hv_remove_kexec_handler);
> -
> void __weak hv_setup_crash_handler(void (*handler)(struct pt_regs *regs))
> {
> }
> diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
> index 5ebdbe24b5a1..3532ea779d34 100644
> --- a/drivers/hv/vmbus_drv.c
> +++ b/drivers/hv/vmbus_drv.c
> @@ -33,6 +33,7 @@
> #include <linux/sysfb.h>
> #include <linux/efi.h>
> #include <linux/kernel.h>
> +#include <linux/kexec.h>
> #include <linux/syscore_ops.h>
> #include <linux/dma-map-ops.h>
> #include <linux/pci.h>
> @@ -2904,6 +2905,12 @@ static void hv_kexec_handler(void)
> cpuhp_remove_state(hyperv_cpuhp_online);
> };
>
> +static void hv_vmbus_syscore_shutdown(void *data)
> +{
> + if (kexec_is_in_progress())
> + hv_kexec_handler();
> +}
> +
> static void hv_crash_handler(struct pt_regs *regs)
> {
> int cpu;
> @@ -2958,14 +2965,15 @@ static void hv_synic_resume(void *data)
> */
> }
>
> -/* The callbacks run only on CPU0, with irqs_disabled. */
> -static const struct syscore_ops hv_synic_syscore_ops = {
> +/* The suspend and resume callbacks run only on CPU0, with irqs_disabled. */
> +static const struct syscore_ops hv_vmbus_syscore_ops = {
> .suspend = hv_synic_suspend,
> .resume = hv_synic_resume,
> + .shutdown = hv_vmbus_syscore_shutdown,
> };
>
> -static struct syscore hv_synic_syscore = {
> - .ops = &hv_synic_syscore_ops,
> +static struct syscore hv_vmbus_syscore = {
> + .ops = &hv_vmbus_syscore_ops,
> };
>
> static int __init hv_acpi_init(void)
> @@ -3014,10 +3022,9 @@ static int __init hv_acpi_init(void)
> if (ret)
> goto cleanup;
>
> - hv_setup_kexec_handler(hv_kexec_handler);
> hv_setup_crash_handler(hv_crash_handler);
>
> - register_syscore(&hv_synic_syscore);
> + register_syscore(&hv_vmbus_syscore);
>
> return 0;
>
> @@ -3039,9 +3046,8 @@ static void __exit vmbus_exit(void)
> */
> return;
>
> - unregister_syscore(&hv_synic_syscore);
> + unregister_syscore(&hv_vmbus_syscore);
>
> - hv_remove_kexec_handler();
> hv_remove_crash_handler();
> vmbus_connection.conn_state = DISCONNECTED;
> hv_stimer_global_cleanup();
> diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h
> index bf601d67cecb..98fe5c0f54ea 100644
> --- a/include/asm-generic/mshyperv.h
> +++ b/include/asm-generic/mshyperv.h
> @@ -183,8 +183,6 @@ void hv_remove_vmbus_handler(void);
> void hv_setup_stimer0_handler(void (*handler)(void));
> void hv_remove_stimer0_handler(void);
>
> -void hv_setup_kexec_handler(void (*handler)(void));
> -void hv_remove_kexec_handler(void);
> void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs));
> void hv_remove_crash_handler(void);
> void hv_setup_mshv_handler(void (*handler)(void));
--
Regards,
Mukesh Pilaniya
More information about the kexec
mailing list