]> www.infradead.org Git - linux-platform-drivers-x86.git/commitdiff
Drivers: hv: vmbus: Move handling of VMbus interrupts
authorMichael Kelley <mikelley@microsoft.com>
Tue, 2 Mar 2021 21:38:18 +0000 (13:38 -0800)
committerWei Liu <wei.liu@kernel.org>
Mon, 8 Mar 2021 17:33:00 +0000 (17:33 +0000)
VMbus interrupts are most naturally modelled as per-cpu IRQs.  But
because x86/x64 doesn't have per-cpu IRQs, the core VMbus interrupt
handling machinery is done in code under arch/x86 and Linux IRQs are
not used.  Adding support for ARM64 means adding equivalent code
using per-cpu IRQs under arch/arm64.

A better model is to treat per-cpu IRQs as the normal path (which it is
for modern architectures), and the x86/x64 path as the exception.  Do this
by incorporating standard Linux per-cpu IRQ allocation into the main VMbus
driver, and bypassing it in the x86/x64 exception case. For x86/x64,
special case code is retained under arch/x86, but no VMbus interrupt
handling code is needed under arch/arm64.

No functional change.

Signed-off-by: Michael Kelley <mikelley@microsoft.com>
Reviewed-by: Boqun Feng <boqun.feng@gmail.com>
Link: https://lore.kernel.org/r/1614721102-2241-7-git-send-email-mikelley@microsoft.com
Signed-off-by: Wei Liu <wei.liu@kernel.org>
arch/x86/include/asm/mshyperv.h
arch/x86/kernel/cpu/mshyperv.c
drivers/hv/hv.c
drivers/hv/vmbus_drv.c
include/asm-generic/mshyperv.h

index a6c608df0217b72a43567d51138e028e85a48d68..c10dd1c9ed816caa9624bbb5a4ed65b9df547ba7 100644 (file)
@@ -32,7 +32,6 @@ static inline u64 hv_get_register(unsigned int reg)
 #define hv_enable_vdso_clocksource() \
        vclocks_set_used(VDSO_CLOCKMODE_HVCLOCK);
 #define hv_get_raw_timer() rdtsc_ordered()
-#define hv_get_vector() HYPERVISOR_CALLBACK_VECTOR
 
 /*
  * Reference to pv_ops must be inline so objtool
index e88bc296afca0ba9f513b37ceb336396b0431a92..41fd84a887833a1dda2f986911b1268f887eb3c8 100644 (file)
@@ -60,23 +60,18 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_callback)
        set_irq_regs(old_regs);
 }
 
-int hv_setup_vmbus_irq(int irq, void (*handler)(void))
+void hv_setup_vmbus_handler(void (*handler)(void))
 {
-       /*
-        * The 'irq' argument is ignored on x86/x64 because a hard-coded
-        * interrupt vector is used for Hyper-V interrupts.
-        */
        vmbus_handler = handler;
-       return 0;
 }
+EXPORT_SYMBOL_GPL(hv_setup_vmbus_handler);
 
-void hv_remove_vmbus_irq(void)
+void hv_remove_vmbus_handler(void)
 {
        /* We have no way to deallocate the interrupt gate */
        vmbus_handler = NULL;
 }
-EXPORT_SYMBOL_GPL(hv_setup_vmbus_irq);
-EXPORT_SYMBOL_GPL(hv_remove_vmbus_irq);
+EXPORT_SYMBOL_GPL(hv_remove_vmbus_handler);
 
 /*
  * Routines to do per-architecture handling of stimer0
index afe7a62b447f41ce1fee71c883c20ba855bbd7c5..917b29e873c51313b52a7c966da3327d24fc55c5 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/version.h>
 #include <linux/random.h>
 #include <linux/clockchips.h>
+#include <linux/interrupt.h>
 #include <clocksource/hyperv_timer.h>
 #include <asm/mshyperv.h>
 #include "hyperv_vmbus.h"
@@ -214,10 +215,12 @@ void hv_synic_enable_regs(unsigned int cpu)
        hv_set_register(HV_REGISTER_SIEFP, siefp.as_uint64);
 
        /* Setup the shared SINT. */
+       if (vmbus_irq != -1)
+               enable_percpu_irq(vmbus_irq, 0);
        shared_sint.as_uint64 = hv_get_register(HV_REGISTER_SINT0 +
                                        VMBUS_MESSAGE_SINT);
 
-       shared_sint.vector = hv_get_vector();
+       shared_sint.vector = vmbus_interrupt;
        shared_sint.masked = false;
 
        /*
@@ -285,6 +288,9 @@ void hv_synic_disable_regs(unsigned int cpu)
        sctrl.as_uint64 = hv_get_register(HV_REGISTER_SCONTROL);
        sctrl.enable = 0;
        hv_set_register(HV_REGISTER_SCONTROL, sctrl.as_uint64);
+
+       if (vmbus_irq != -1)
+               disable_percpu_irq(vmbus_irq);
 }
 
 
index 7524d71a0f8401f7c1f25e94198604170dd8b328..51c40d5e3c8ac8c3921debf6c12219177e95a1c0 100644 (file)
@@ -48,8 +48,10 @@ static int hyperv_cpuhp_online;
 
 static void *hv_panic_page;
 
+static long __percpu *vmbus_evt;
+
 /* Values parsed from ACPI DSDT */
-static int vmbus_irq;
+int vmbus_irq;
 int vmbus_interrupt;
 
 /*
@@ -1381,7 +1383,13 @@ static void vmbus_isr(void)
                        tasklet_schedule(&hv_cpu->msg_dpc);
        }
 
-       add_interrupt_randomness(hv_get_vector(), 0);
+       add_interrupt_randomness(vmbus_interrupt, 0);
+}
+
+static irqreturn_t vmbus_percpu_isr(int irq, void *dev_id)
+{
+       vmbus_isr();
+       return IRQ_HANDLED;
 }
 
 /*
@@ -1496,9 +1504,28 @@ static int vmbus_bus_init(void)
        if (ret)
                return ret;
 
-       ret = hv_setup_vmbus_irq(vmbus_irq, vmbus_isr);
-       if (ret)
-               goto err_setup;
+       /*
+        * VMbus interrupts are best modeled as per-cpu interrupts. If
+        * on an architecture with support for per-cpu IRQs (e.g. ARM64),
+        * allocate a per-cpu IRQ using standard Linux kernel functionality.
+        * If not on such an architecture (e.g., x86/x64), then rely on
+        * code in the arch-specific portion of the code tree to connect
+        * the VMbus interrupt handler.
+        */
+
+       if (vmbus_irq == -1) {
+               hv_setup_vmbus_handler(vmbus_isr);
+       } else {
+               vmbus_evt = alloc_percpu(long);
+               ret = request_percpu_irq(vmbus_irq, vmbus_percpu_isr,
+                               "Hyper-V VMbus", vmbus_evt);
+               if (ret) {
+                       pr_err("Can't request Hyper-V VMbus IRQ %d, Err %d",
+                                       vmbus_irq, ret);
+                       free_percpu(vmbus_evt);
+                       goto err_setup;
+               }
+       }
 
        ret = hv_synic_alloc();
        if (ret)
@@ -1559,7 +1586,12 @@ err_connect:
 err_cpuhp:
        hv_synic_free();
 err_alloc:
-       hv_remove_vmbus_irq();
+       if (vmbus_irq == -1) {
+               hv_remove_vmbus_handler();
+       } else {
+               free_percpu_irq(vmbus_irq, vmbus_evt);
+               free_percpu(vmbus_evt);
+       }
 err_setup:
        bus_unregister(&hv_bus);
        unregister_sysctl_table(hv_ctl_table_hdr);
@@ -2677,6 +2709,18 @@ static int __init hv_acpi_init(void)
                ret = -ETIMEDOUT;
                goto cleanup;
        }
+
+       /*
+        * If we're on an architecture with a hardcoded hypervisor
+        * vector (i.e. x86/x64), override the VMbus interrupt found
+        * in the ACPI tables. Ensure vmbus_irq is not set since the
+        * normal Linux IRQ mechanism is not used in this case.
+        */
+#ifdef HYPERVISOR_CALLBACK_VECTOR
+       vmbus_interrupt = HYPERVISOR_CALLBACK_VECTOR;
+       vmbus_irq = -1;
+#endif
+
        hv_debug_init();
 
        ret = vmbus_bus_init();
@@ -2707,7 +2751,12 @@ static void __exit vmbus_exit(void)
        vmbus_connection.conn_state = DISCONNECTED;
        hv_stimer_global_cleanup();
        vmbus_disconnect();
-       hv_remove_vmbus_irq();
+       if (vmbus_irq == -1) {
+               hv_remove_vmbus_handler();
+       } else {
+               free_percpu_irq(vmbus_irq, vmbus_evt);
+               free_percpu(vmbus_evt);
+       }
        for_each_online_cpu(cpu) {
                struct hv_per_cpu_context *hv_cpu
                        = per_cpu_ptr(hv_context.cpu_context, cpu);
index 70b798d069d939937d743ca84ed5531f5e075b57..43dc3715a98aca23154ec8decb44120678ed3766 100644 (file)
@@ -92,10 +92,8 @@ static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type)
        }
 }
 
-int hv_setup_vmbus_irq(int irq, void (*handler)(void));
-void hv_remove_vmbus_irq(void);
-void hv_enable_vmbus_irq(void);
-void hv_disable_vmbus_irq(void);
+void hv_setup_vmbus_handler(void (*handler)(void));
+void hv_remove_vmbus_handler(void);
 
 void hv_setup_kexec_handler(void (*handler)(void));
 void hv_remove_kexec_handler(void);
@@ -103,6 +101,7 @@ void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs));
 void hv_remove_crash_handler(void);
 
 extern int vmbus_interrupt;
+extern int vmbus_irq;
 
 #if IS_ENABLED(CONFIG_HYPERV)
 /*