--- /dev/null
+From 9afc1d6f5aa8bce4c67532b35fd6219c7064bd7a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 16 Jan 2021 14:31:36 -0800
+Subject: x86/hyperv: Initialize clockevents after LAPIC is initialized
+
+From: Dexuan Cui <decui@microsoft.com>
+
+[ Upstream commit fff7b5e6ee63c5d20406a131b260c619cdd24fd1 ]
+
+With commit 4df4cb9e99f8, the Hyper-V direct-mode STIMER is actually
+initialized before LAPIC is initialized: see
+
+ apic_intr_mode_init()
+
+ x86_platform.apic_post_init()
+ hyperv_init()
+ hv_stimer_alloc()
+
+ apic_bsp_setup()
+ setup_local_APIC()
+
+setup_local_APIC() temporarily disables LAPIC, initializes it and
+re-eanble it. The direct-mode STIMER depends on LAPIC, and when it's
+registered, it can be programmed immediately and the timer can fire
+very soon:
+
+ hv_stimer_init
+ clockevents_config_and_register
+ clockevents_register_device
+ tick_check_new_device
+ tick_setup_device
+ tick_setup_periodic(), tick_setup_oneshot()
+ clockevents_program_event
+
+When the timer fires in the hypervisor, if the LAPIC is in the
+disabled state, new versions of Hyper-V ignore the event and don't inject
+the timer interrupt into the VM, and hence the VM hangs when it boots.
+
+Note: when the VM starts/reboots, the LAPIC is pre-enabled by the
+firmware, so the window of LAPIC being temporarily disabled is pretty
+small, and the issue can only happen once out of 100~200 reboots for
+a 40-vCPU VM on one dev host, and on another host the issue doesn't
+reproduce after 2000 reboots.
+
+The issue is more noticeable for kdump/kexec, because the LAPIC is
+disabled by the first kernel, and stays disabled until the kdump/kexec
+kernel enables it. This is especially an issue to a Generation-2 VM
+(for which Hyper-V doesn't emulate the PIT timer) when CONFIG_HZ=1000
+(rather than CONFIG_HZ=250) is used.
+
+Fix the issue by moving hv_stimer_alloc() to a later place where the
+LAPIC timer is initialized.
+
+Fixes: 4df4cb9e99f8 ("x86/hyperv: Initialize clockevents earlier in CPU onlining")
+Signed-off-by: Dexuan Cui <decui@microsoft.com>
+Reviewed-by: Michael Kelley <mikelley@microsoft.com>
+Link: https://lore.kernel.org/r/20210116223136.13892-1-decui@microsoft.com
+Signed-off-by: Wei Liu <wei.liu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/x86/hyperv/hv_init.c | 29 ++++++++++++++++++++++++++---
+ 1 file changed, 26 insertions(+), 3 deletions(-)
+
+diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c
+index e04d90af4c27c..6fb8cb7b9bcc6 100644
+--- a/arch/x86/hyperv/hv_init.c
++++ b/arch/x86/hyperv/hv_init.c
+@@ -312,6 +312,25 @@ static struct syscore_ops hv_syscore_ops = {
+ .resume = hv_resume,
+ };
+
++static void (* __initdata old_setup_percpu_clockev)(void);
++
++static void __init hv_stimer_setup_percpu_clockev(void)
++{
++ /*
++ * Ignore any errors in setting up stimer clockevents
++ * as we can run with the LAPIC timer as a fallback.
++ */
++ (void)hv_stimer_alloc();
++
++ /*
++ * Still register the LAPIC timer, because the direct-mode STIMER is
++ * not supported by old versions of Hyper-V. This also allows users
++ * to switch to LAPIC timer via /sys, if they want to.
++ */
++ if (old_setup_percpu_clockev)
++ old_setup_percpu_clockev();
++}
++
+ /*
+ * This function is to be invoked early in the boot sequence after the
+ * hypervisor has been detected.
+@@ -390,10 +409,14 @@ void __init hyperv_init(void)
+ wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
+
+ /*
+- * Ignore any errors in setting up stimer clockevents
+- * as we can run with the LAPIC timer as a fallback.
++ * hyperv_init() is called before LAPIC is initialized: see
++ * apic_intr_mode_init() -> x86_platform.apic_post_init() and
++ * apic_bsp_setup() -> setup_local_APIC(). The direct-mode STIMER
++ * depends on LAPIC, so hv_stimer_alloc() should be called from
++ * x86_init.timers.setup_percpu_clockev.
+ */
+- (void)hv_stimer_alloc();
++ old_setup_percpu_clockev = x86_init.timers.setup_percpu_clockev;
++ x86_init.timers.setup_percpu_clockev = hv_stimer_setup_percpu_clockev;
+
+ hv_apic_init();
+
+--
+2.27.0
+