Merge tag 'printk-for-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/printk...
[platform/kernel/linux-rpi.git] / drivers / hv / vmbus_drv.c
index b341b14..92cb3f7 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;
 }
 
 /*
@@ -1393,14 +1401,11 @@ static void hv_kmsg_dump(struct kmsg_dumper *dumper,
 {
        struct kmsg_dump_iter iter;
        size_t bytes_written;
-       phys_addr_t panic_pa;
 
        /* We are only interested in panics. */
        if ((reason != KMSG_DUMP_PANIC) || (!sysctl_record_panic_msg))
                return;
 
-       panic_pa = virt_to_phys(hv_panic_page);
-
        /*
         * Write dump contents to the page. No need to synchronize; panic should
         * be single-threaded.
@@ -1408,8 +1413,25 @@ static void hv_kmsg_dump(struct kmsg_dumper *dumper,
        kmsg_dump_rewind(&iter);
        kmsg_dump_get_buffer(&iter, false, hv_panic_page, HV_HYP_PAGE_SIZE,
                             &bytes_written);
-       if (bytes_written)
-               hyperv_report_panic_msg(panic_pa, bytes_written);
+       if (!bytes_written)
+               return;
+       /*
+        * P3 to contain the physical address of the panic page & P4 to
+        * contain the size of the panic data in that page. Rest of the
+        * registers are no-op when the NOTIFY_MSG flag is set.
+        */
+       hv_set_register(HV_REGISTER_CRASH_P0, 0);
+       hv_set_register(HV_REGISTER_CRASH_P1, 0);
+       hv_set_register(HV_REGISTER_CRASH_P2, 0);
+       hv_set_register(HV_REGISTER_CRASH_P3, virt_to_phys(hv_panic_page));
+       hv_set_register(HV_REGISTER_CRASH_P4, bytes_written);
+
+       /*
+        * Let Hyper-V know there is crash data available along with
+        * the panic message.
+        */
+       hv_set_register(HV_REGISTER_CRASH_CTL,
+              (HV_CRASH_CTL_CRASH_NOTIFY | HV_CRASH_CTL_CRASH_NOTIFY_MSG));
 }
 
 static struct kmsg_dumper hv_kmsg_dumper = {
@@ -1484,9 +1506,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)
@@ -1523,7 +1564,7 @@ static int vmbus_bus_init(void)
                 * Register for panic kmsg callback only if the right
                 * capability is supported by the hypervisor.
                 */
-               hv_get_crash_ctl(hyperv_crash_ctl);
+               hyperv_crash_ctl = hv_get_register(HV_REGISTER_CRASH_CTL);
                if (hyperv_crash_ctl & HV_CRASH_CTL_CRASH_NOTIFY_MSG)
                        hv_kmsg_dump_register();
 
@@ -1547,7 +1588,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);
@@ -1804,13 +1850,15 @@ static ssize_t target_cpu_store(struct vmbus_channel *channel,
        if (target_cpu == origin_cpu)
                goto cpu_store_unlock;
 
-       if (vmbus_send_modifychannel(channel->offermsg.child_relid,
+       if (vmbus_send_modifychannel(channel,
                                     hv_cpu_number_to_vp_number(target_cpu))) {
                ret = -EIO;
                goto cpu_store_unlock;
        }
 
        /*
+        * For version before VERSION_WIN10_V5_3, the following warning holds:
+        *
         * Warning.  At this point, there is *no* guarantee that the host will
         * have successfully processed the vmbus_send_modifychannel() request.
         * See the header comment of vmbus_send_modifychannel() for more info.
@@ -2665,6 +2713,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();
@@ -2695,7 +2755,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);