aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2014-02-23 16:40:22 -0500
committerThomas Gleixner <tglx@linutronix.de>2014-03-04 11:37:54 -0500
commit1aec169673d7db113c37367bbc371c2ba8109f06 (patch)
treef4266504ea668ed1b47300383875a96b50f58d92
parent99c8b79d3c165f8e2a6247c14bfa1429e7efe51f (diff)
x86: Hyperv: Cleanup the irq mess
The vmbus/hyperv interrupt handling is another complete trainwreck and probably the worst of all currently in tree. If CONFIG_HYPERV=y then the interrupt delivery to the vmbus happens via the direct HYPERVISOR_CALLBACK_VECTOR. So far so good, but: The driver requests first a normal device interrupt. The only reason to do so is to increment the interrupt stats of that device interrupt. For no reason it also installs a private flow handler. We have proper accounting mechanisms for direct vectors, but of course it's too much effort to add that 5 lines of code. Aside of that the alloc_intr_gate() is not protected against reallocation which makes module reload impossible. Solution to the problem is simple to rip out the whole mess and implement it correctly. First of all move all that code to arch/x86/kernel/cpu/mshyperv.c and merily install the HYPERVISOR_CALLBACK_VECTOR with proper reallocation protection and use the proper direct vector accounting mechanism. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Acked-by: K. Y. Srinivasan <kys@microsoft.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: linuxdrivers <devel@linuxdriverproject.org> Cc: x86 <x86@kernel.org> Link: http://lkml.kernel.org/r/20140223212739.028307673@linutronix.de Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r--arch/x86/include/asm/mshyperv.h4
-rw-r--r--arch/x86/kernel/cpu/mshyperv.c78
-rw-r--r--drivers/hv/vmbus_drv.c39
3 files changed, 47 insertions, 74 deletions
diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h
index cd9c41938b8a..e98f66f35635 100644
--- a/arch/x86/include/asm/mshyperv.h
+++ b/arch/x86/include/asm/mshyperv.h
@@ -2,6 +2,7 @@
2#define _ASM_X86_MSHYPER_H 2#define _ASM_X86_MSHYPER_H
3 3
4#include <linux/types.h> 4#include <linux/types.h>
5#include <linux/interrupt.h>
5#include <asm/hyperv.h> 6#include <asm/hyperv.h>
6 7
7struct ms_hyperv_info { 8struct ms_hyperv_info {
@@ -16,6 +17,7 @@ void hyperv_callback_vector(void);
16#define trace_hyperv_callback_vector hyperv_callback_vector 17#define trace_hyperv_callback_vector hyperv_callback_vector
17#endif 18#endif
18void hyperv_vector_handler(struct pt_regs *regs); 19void hyperv_vector_handler(struct pt_regs *regs);
19void hv_register_vmbus_handler(int irq, irq_handler_t handler); 20int hv_setup_vmbus_irq(int irq, irq_handler_t handler, void *dev_id);
21void hv_remove_vmbus_irq(int irq, void *dev_id);
20 22
21#endif 23#endif
diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c
index 9f7ca266864a..1bd316cd32c8 100644
--- a/arch/x86/kernel/cpu/mshyperv.c
+++ b/arch/x86/kernel/cpu/mshyperv.c
@@ -17,6 +17,7 @@
17#include <linux/hardirq.h> 17#include <linux/hardirq.h>
18#include <linux/efi.h> 18#include <linux/efi.h>
19#include <linux/interrupt.h> 19#include <linux/interrupt.h>
20#include <linux/irq.h>
20#include <asm/processor.h> 21#include <asm/processor.h>
21#include <asm/hypervisor.h> 22#include <asm/hypervisor.h>
22#include <asm/hyperv.h> 23#include <asm/hyperv.h>
@@ -30,6 +31,45 @@
30struct ms_hyperv_info ms_hyperv; 31struct ms_hyperv_info ms_hyperv;
31EXPORT_SYMBOL_GPL(ms_hyperv); 32EXPORT_SYMBOL_GPL(ms_hyperv);
32 33
34#ifdef CONFIG_HYPERV
35static irq_handler_t *vmbus_handler;
36
37void hyperv_vector_handler(struct pt_regs *regs)
38{
39 struct pt_regs *old_regs = set_irq_regs(regs);
40
41 irq_enter();
42 exit_idle();
43
44 inc_irq_stat(irq_hv_callback_count);
45 if (vmbus_handler)
46 vmbus_handler();
47
48 irq_exit();
49 set_irq_regs(old_regs);
50}
51
52int hv_setup_vmbus_irq(int irq, irq_handler_t *handler, void *dev_id)
53{
54 vmbus_handler = handler;
55 /*
56 * Setup the IDT for hypervisor callback. Prevent reallocation
57 * at module reload.
58 */
59 if (!test_bit(HYPERVISOR_CALLBACK_VECTOR, used_vectors))
60 alloc_intr_gate(HYPERVISOR_CALLBACK_VECTOR,
61 hyperv_callback_vector);
62}
63
64void hv_remove_vmbus_irq(unsigned int irq, void *dev_id)
65{
66 /* We have no way to deallocate the interrupt gate */
67 vmbus_handler = NULL;
68}
69EXPORT_SYMBOL_GPL(hv_setup_vmbus_irq);
70EXPORT_SYMBOL_GPL(hv_remove_vmbus_irq);
71#endif
72
33static uint32_t __init ms_hyperv_platform(void) 73static uint32_t __init ms_hyperv_platform(void)
34{ 74{
35 u32 eax; 75 u32 eax;
@@ -113,41 +153,3 @@ const __refconst struct hypervisor_x86 x86_hyper_ms_hyperv = {
113 .init_platform = ms_hyperv_init_platform, 153 .init_platform = ms_hyperv_init_platform,
114}; 154};
115EXPORT_SYMBOL(x86_hyper_ms_hyperv); 155EXPORT_SYMBOL(x86_hyper_ms_hyperv);
116
117#if IS_ENABLED(CONFIG_HYPERV)
118static int vmbus_irq = -1;
119static irq_handler_t vmbus_isr;
120
121void hv_register_vmbus_handler(int irq, irq_handler_t handler)
122{
123 /*
124 * Setup the IDT for hypervisor callback.
125 */
126 alloc_intr_gate(HYPERVISOR_CALLBACK_VECTOR, hyperv_callback_vector);
127
128 vmbus_irq = irq;
129 vmbus_isr = handler;
130}
131
132void hyperv_vector_handler(struct pt_regs *regs)
133{
134 struct pt_regs *old_regs = set_irq_regs(regs);
135 struct irq_desc *desc;
136
137 irq_enter();
138 exit_idle();
139
140 desc = irq_to_desc(vmbus_irq);
141
142 if (desc)
143 generic_handle_irq_desc(vmbus_irq, desc);
144
145 irq_exit();
146 set_irq_regs(old_regs);
147}
148#else
149void hv_register_vmbus_handler(int irq, irq_handler_t handler)
150{
151}
152#endif
153EXPORT_SYMBOL_GPL(hv_register_vmbus_handler);
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index 077bb1bdac34..5a6909fff1c1 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -25,7 +25,6 @@
25#include <linux/init.h> 25#include <linux/init.h>
26#include <linux/module.h> 26#include <linux/module.h>
27#include <linux/device.h> 27#include <linux/device.h>
28#include <linux/irq.h>
29#include <linux/interrupt.h> 28#include <linux/interrupt.h>
30#include <linux/sysctl.h> 29#include <linux/sysctl.h>
31#include <linux/slab.h> 30#include <linux/slab.h>
@@ -558,9 +557,6 @@ static struct bus_type hv_bus = {
558 .dev_groups = vmbus_groups, 557 .dev_groups = vmbus_groups,
559}; 558};
560 559
561static const char *driver_name = "hyperv";
562
563
564struct onmessage_work_context { 560struct onmessage_work_context {
565 struct work_struct work; 561 struct work_struct work;
566 struct hv_message msg; 562 struct hv_message msg;
@@ -677,19 +673,6 @@ static irqreturn_t vmbus_isr(int irq, void *dev_id)
677} 673}
678 674
679/* 675/*
680 * vmbus interrupt flow handler:
681 * vmbus interrupts can concurrently occur on multiple CPUs and
682 * can be handled concurrently.
683 */
684
685static void vmbus_flow_handler(unsigned int irq, struct irq_desc *desc)
686{
687 kstat_incr_irqs_this_cpu(irq, desc);
688
689 desc->action->handler(irq, desc->action->dev_id);
690}
691
692/*
693 * vmbus_bus_init -Main vmbus driver initialization routine. 676 * vmbus_bus_init -Main vmbus driver initialization routine.
694 * 677 *
695 * Here, we 678 * Here, we
@@ -715,26 +698,13 @@ static int vmbus_bus_init(int irq)
715 if (ret) 698 if (ret)
716 goto err_cleanup; 699 goto err_cleanup;
717 700
718 ret = request_irq(irq, vmbus_isr, 0, driver_name, hv_acpi_dev); 701 ret = hv_setup_vmbus_irq(irq, vmbus_isr, hv_acpi_dev);
719 702
720 if (ret != 0) { 703 if (ret != 0) {
721 pr_err("Unable to request IRQ %d\n", 704 pr_err("Unable to request IRQ %d\n", irq);
722 irq);
723 goto err_unregister; 705 goto err_unregister;
724 } 706 }
725 707
726 /*
727 * Vmbus interrupts can be handled concurrently on
728 * different CPUs. Establish an appropriate interrupt flow
729 * handler that can support this model.
730 */
731 irq_set_handler(irq, vmbus_flow_handler);
732
733 /*
734 * Register our interrupt handler.
735 */
736 hv_register_vmbus_handler(irq, vmbus_isr);
737
738 ret = hv_synic_alloc(); 708 ret = hv_synic_alloc();
739 if (ret) 709 if (ret)
740 goto err_alloc; 710 goto err_alloc;
@@ -753,7 +723,7 @@ static int vmbus_bus_init(int irq)
753 723
754err_alloc: 724err_alloc:
755 hv_synic_free(); 725 hv_synic_free();
756 free_irq(irq, hv_acpi_dev); 726 hv_remove_vmbus_irq(irq, hv_acpi_dev);
757 727
758err_unregister: 728err_unregister:
759 bus_unregister(&hv_bus); 729 bus_unregister(&hv_bus);
@@ -978,8 +948,7 @@ cleanup:
978 948
979static void __exit vmbus_exit(void) 949static void __exit vmbus_exit(void)
980{ 950{
981 951 hv_remove_vmbus_irq(irq, hv_acpi_dev);
982 free_irq(irq, hv_acpi_dev);
983 vmbus_free_channels(); 952 vmbus_free_channels();
984 bus_unregister(&hv_bus); 953 bus_unregister(&hv_bus);
985 hv_cleanup(); 954 hv_cleanup();