aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefano Stabellini <stefano.stabellini@eu.citrix.com>2010-12-01 09:51:44 -0500
committerStefano Stabellini <stefano.stabellini@eu.citrix.com>2010-12-02 09:34:25 -0500
commitaf42b8d12f8adec6711cb824549a0edac6a4ae8f (patch)
treee922110d01d85688a03f9ae5d7e31c9f73f2b001
parente5fc7345412d5e4758fcef55a74354c5cbefd61e (diff)
xen: fix MSI setup and teardown for PV on HVM guests
When remapping MSIs into pirqs for PV on HVM guests, qemu is responsible for doing the actual mapping and unmapping. We only give qemu the desired pirq number when we ask to do the mapping the first time, after that we should be reading back the pirq number from qemu every time we want to re-enable the MSI. This fixes a bug in xen_hvm_setup_msi_irqs that manifests itself when trying to enable the same MSI for the second time: the old MSI to pirq mapping is still valid at this point but xen_hvm_setup_msi_irqs would try to assign a new pirq anyway. A simple way to reproduce this bug is to assign an MSI capable network card to a PV on HVM guest, if the user brings down the corresponding ethernet interface and up again, Linux would fail to enable MSIs on the device. Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
-rw-r--r--arch/x86/pci/xen.c27
-rw-r--r--drivers/xen/events.c24
-rw-r--r--include/xen/events.h7
3 files changed, 43 insertions, 15 deletions
diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c
index d7b5109f7a9c..25cd4a07d09f 100644
--- a/arch/x86/pci/xen.c
+++ b/arch/x86/pci/xen.c
@@ -70,6 +70,9 @@ static int acpi_register_gsi_xen_hvm(struct device *dev, u32 gsi,
70struct xen_pci_frontend_ops *xen_pci_frontend; 70struct xen_pci_frontend_ops *xen_pci_frontend;
71EXPORT_SYMBOL_GPL(xen_pci_frontend); 71EXPORT_SYMBOL_GPL(xen_pci_frontend);
72 72
73#define XEN_PIRQ_MSI_DATA (MSI_DATA_TRIGGER_EDGE | \
74 MSI_DATA_LEVEL_ASSERT | (3 << 8) | MSI_DATA_VECTOR(0))
75
73static void xen_msi_compose_msg(struct pci_dev *pdev, unsigned int pirq, 76static void xen_msi_compose_msg(struct pci_dev *pdev, unsigned int pirq,
74 struct msi_msg *msg) 77 struct msi_msg *msg)
75{ 78{
@@ -83,12 +86,7 @@ static void xen_msi_compose_msg(struct pci_dev *pdev, unsigned int pirq,
83 MSI_ADDR_REDIRECTION_CPU | 86 MSI_ADDR_REDIRECTION_CPU |
84 MSI_ADDR_DEST_ID(pirq); 87 MSI_ADDR_DEST_ID(pirq);
85 88
86 msg->data = 89 msg->data = XEN_PIRQ_MSI_DATA;
87 MSI_DATA_TRIGGER_EDGE |
88 MSI_DATA_LEVEL_ASSERT |
89 /* delivery mode reserved */
90 (3 << 8) |
91 MSI_DATA_VECTOR(0);
92} 90}
93 91
94static int xen_hvm_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) 92static int xen_hvm_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
@@ -98,8 +96,23 @@ static int xen_hvm_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
98 struct msi_msg msg; 96 struct msi_msg msg;
99 97
100 list_for_each_entry(msidesc, &dev->msi_list, list) { 98 list_for_each_entry(msidesc, &dev->msi_list, list) {
99 __read_msi_msg(msidesc, &msg);
100 pirq = MSI_ADDR_EXT_DEST_ID(msg.address_hi) |
101 ((msg.address_lo >> MSI_ADDR_DEST_ID_SHIFT) & 0xff);
102 if (xen_irq_from_pirq(pirq) >= 0 && msg.data == XEN_PIRQ_MSI_DATA) {
103 xen_allocate_pirq_msi((type == PCI_CAP_ID_MSIX) ?
104 "msi-x" : "msi", &irq, &pirq, XEN_ALLOC_IRQ);
105 if (irq < 0)
106 goto error;
107 ret = set_irq_msi(irq, msidesc);
108 if (ret < 0)
109 goto error_while;
110 printk(KERN_DEBUG "xen: msi already setup: msi --> irq=%d"
111 " pirq=%d\n", irq, pirq);
112 return 0;
113 }
101 xen_allocate_pirq_msi((type == PCI_CAP_ID_MSIX) ? 114 xen_allocate_pirq_msi((type == PCI_CAP_ID_MSIX) ?
102 "msi-x" : "msi", &irq, &pirq); 115 "msi-x" : "msi", &irq, &pirq, (XEN_ALLOC_IRQ | XEN_ALLOC_PIRQ));
103 if (irq < 0 || pirq < 0) 116 if (irq < 0 || pirq < 0)
104 goto error; 117 goto error;
105 printk(KERN_DEBUG "xen: msi --> irq=%d, pirq=%d\n", irq, pirq); 118 printk(KERN_DEBUG "xen: msi --> irq=%d, pirq=%d\n", irq, pirq);
diff --git a/drivers/xen/events.c b/drivers/xen/events.c
index 7ab43c33f746..f78945ce8aeb 100644
--- a/drivers/xen/events.c
+++ b/drivers/xen/events.c
@@ -668,17 +668,21 @@ out:
668#include <linux/msi.h> 668#include <linux/msi.h>
669#include "../pci/msi.h" 669#include "../pci/msi.h"
670 670
671void xen_allocate_pirq_msi(char *name, int *irq, int *pirq) 671void xen_allocate_pirq_msi(char *name, int *irq, int *pirq, int alloc)
672{ 672{
673 spin_lock(&irq_mapping_update_lock); 673 spin_lock(&irq_mapping_update_lock);
674 674
675 *irq = find_unbound_irq(); 675 if (alloc & XEN_ALLOC_IRQ) {
676 if (*irq == -1) 676 *irq = find_unbound_irq();
677 goto out; 677 if (*irq == -1)
678 goto out;
679 }
678 680
679 *pirq = find_unbound_pirq(MAP_PIRQ_TYPE_MSI); 681 if (alloc & XEN_ALLOC_PIRQ) {
680 if (*pirq == -1) 682 *pirq = find_unbound_pirq(MAP_PIRQ_TYPE_MSI);
681 goto out; 683 if (*pirq == -1)
684 goto out;
685 }
682 686
683 set_irq_chip_and_handler_name(*irq, &xen_pirq_chip, 687 set_irq_chip_and_handler_name(*irq, &xen_pirq_chip,
684 handle_level_irq, name); 688 handle_level_irq, name);
@@ -766,6 +770,7 @@ int xen_destroy_irq(int irq)
766 printk(KERN_WARNING "unmap irq failed %d\n", rc); 770 printk(KERN_WARNING "unmap irq failed %d\n", rc);
767 goto out; 771 goto out;
768 } 772 }
773 pirq_to_irq[info->u.pirq.pirq] = -1;
769 } 774 }
770 irq_info[irq] = mk_unbound_info(); 775 irq_info[irq] = mk_unbound_info();
771 776
@@ -786,6 +791,11 @@ int xen_gsi_from_irq(unsigned irq)
786 return gsi_from_irq(irq); 791 return gsi_from_irq(irq);
787} 792}
788 793
794int xen_irq_from_pirq(unsigned pirq)
795{
796 return pirq_to_irq[pirq];
797}
798
789int bind_evtchn_to_irq(unsigned int evtchn) 799int bind_evtchn_to_irq(unsigned int evtchn)
790{ 800{
791 int irq; 801 int irq;
diff --git a/include/xen/events.h b/include/xen/events.h
index 646dd17d3aa4..00f53ddcc062 100644
--- a/include/xen/events.h
+++ b/include/xen/events.h
@@ -76,7 +76,9 @@ int xen_map_pirq_gsi(unsigned pirq, unsigned gsi, int shareable, char *name);
76 76
77#ifdef CONFIG_PCI_MSI 77#ifdef CONFIG_PCI_MSI
78/* Allocate an irq and a pirq to be used with MSIs. */ 78/* Allocate an irq and a pirq to be used with MSIs. */
79void xen_allocate_pirq_msi(char *name, int *irq, int *pirq); 79#define XEN_ALLOC_PIRQ (1 << 0)
80#define XEN_ALLOC_IRQ (1 << 1)
81void xen_allocate_pirq_msi(char *name, int *irq, int *pirq, int alloc_mask);
80int xen_create_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, int type); 82int xen_create_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, int type);
81#endif 83#endif
82 84
@@ -89,4 +91,7 @@ int xen_vector_from_irq(unsigned pirq);
89/* Return gsi allocated to pirq */ 91/* Return gsi allocated to pirq */
90int xen_gsi_from_irq(unsigned pirq); 92int xen_gsi_from_irq(unsigned pirq);
91 93
94/* Return irq from pirq */
95int xen_irq_from_pirq(unsigned pirq);
96
92#endif /* _XEN_EVENTS_H */ 97#endif /* _XEN_EVENTS_H */