Commit af42b8d12f8adec6711cb824549a0edac6a4ae8f
1 parent
e5fc734541
Exists in
master
and in
39 other branches
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>
Showing 3 changed files with 43 additions and 15 deletions Side-by-side Diff
arch/x86/pci/xen.c
... | ... | @@ -70,6 +70,9 @@ |
70 | 70 | struct xen_pci_frontend_ops *xen_pci_frontend; |
71 | 71 | EXPORT_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 | + | |
73 | 76 | static void xen_msi_compose_msg(struct pci_dev *pdev, unsigned int pirq, |
74 | 77 | struct msi_msg *msg) |
75 | 78 | { |
... | ... | @@ -83,12 +86,7 @@ |
83 | 86 | MSI_ADDR_REDIRECTION_CPU | |
84 | 87 | MSI_ADDR_DEST_ID(pirq); |
85 | 88 | |
86 | - msg->data = | |
87 | - MSI_DATA_TRIGGER_EDGE | | |
88 | - MSI_DATA_LEVEL_ASSERT | | |
89 | - /* delivery mode reserved */ | |
90 | - (3 << 8) | | |
91 | - MSI_DATA_VECTOR(0); | |
89 | + msg->data = XEN_PIRQ_MSI_DATA; | |
92 | 90 | } |
93 | 91 | |
94 | 92 | static int xen_hvm_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) |
95 | 93 | |
... | ... | @@ -98,8 +96,23 @@ |
98 | 96 | struct msi_msg msg; |
99 | 97 | |
100 | 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 | 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 | 116 | if (irq < 0 || pirq < 0) |
104 | 117 | goto error; |
105 | 118 | printk(KERN_DEBUG "xen: msi --> irq=%d, pirq=%d\n", irq, pirq); |
drivers/xen/events.c
... | ... | @@ -668,17 +668,21 @@ |
668 | 668 | #include <linux/msi.h> |
669 | 669 | #include "../pci/msi.h" |
670 | 670 | |
671 | -void xen_allocate_pirq_msi(char *name, int *irq, int *pirq) | |
671 | +void xen_allocate_pirq_msi(char *name, int *irq, int *pirq, int alloc) | |
672 | 672 | { |
673 | 673 | spin_lock(&irq_mapping_update_lock); |
674 | 674 | |
675 | - *irq = find_unbound_irq(); | |
676 | - if (*irq == -1) | |
677 | - goto out; | |
675 | + if (alloc & XEN_ALLOC_IRQ) { | |
676 | + *irq = find_unbound_irq(); | |
677 | + if (*irq == -1) | |
678 | + goto out; | |
679 | + } | |
678 | 680 | |
679 | - *pirq = find_unbound_pirq(MAP_PIRQ_TYPE_MSI); | |
680 | - if (*pirq == -1) | |
681 | - goto out; | |
681 | + if (alloc & XEN_ALLOC_PIRQ) { | |
682 | + *pirq = find_unbound_pirq(MAP_PIRQ_TYPE_MSI); | |
683 | + if (*pirq == -1) | |
684 | + goto out; | |
685 | + } | |
682 | 686 | |
683 | 687 | set_irq_chip_and_handler_name(*irq, &xen_pirq_chip, |
684 | 688 | handle_level_irq, name); |
... | ... | @@ -766,6 +770,7 @@ |
766 | 770 | printk(KERN_WARNING "unmap irq failed %d\n", rc); |
767 | 771 | goto out; |
768 | 772 | } |
773 | + pirq_to_irq[info->u.pirq.pirq] = -1; | |
769 | 774 | } |
770 | 775 | irq_info[irq] = mk_unbound_info(); |
771 | 776 | |
... | ... | @@ -784,6 +789,11 @@ |
784 | 789 | int xen_gsi_from_irq(unsigned irq) |
785 | 790 | { |
786 | 791 | return gsi_from_irq(irq); |
792 | +} | |
793 | + | |
794 | +int xen_irq_from_pirq(unsigned pirq) | |
795 | +{ | |
796 | + return pirq_to_irq[pirq]; | |
787 | 797 | } |
788 | 798 | |
789 | 799 | int bind_evtchn_to_irq(unsigned int evtchn) |
include/xen/events.h
... | ... | @@ -76,7 +76,9 @@ |
76 | 76 | |
77 | 77 | #ifdef CONFIG_PCI_MSI |
78 | 78 | /* Allocate an irq and a pirq to be used with MSIs. */ |
79 | -void xen_allocate_pirq_msi(char *name, int *irq, int *pirq); | |
79 | +#define XEN_ALLOC_PIRQ (1 << 0) | |
80 | +#define XEN_ALLOC_IRQ (1 << 1) | |
81 | +void xen_allocate_pirq_msi(char *name, int *irq, int *pirq, int alloc_mask); | |
80 | 82 | int xen_create_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, int type); |
81 | 83 | #endif |
82 | 84 | |
... | ... | @@ -88,6 +90,9 @@ |
88 | 90 | |
89 | 91 | /* Return gsi allocated to pirq */ |
90 | 92 | int xen_gsi_from_irq(unsigned pirq); |
93 | + | |
94 | +/* Return irq from pirq */ | |
95 | +int xen_irq_from_pirq(unsigned pirq); | |
91 | 96 | |
92 | 97 | #endif /* _XEN_EVENTS_H */ |