Commit af42b8d12f8adec6711cb824549a0edac6a4ae8f

Authored by Stefano Stabellini
1 parent e5fc734541

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

... ... @@ -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 */