vpci/msi: fix update of bound MSI interrupts
authorRoger Pau Monné <roger.pau@citrix.com>
Mon, 2 Jul 2018 11:07:55 +0000 (13:07 +0200)
committerJan Beulich <jbeulich@suse.com>
Mon, 2 Jul 2018 11:07:55 +0000 (13:07 +0200)
Current update process of already bound MSI interrupts is wrong
because unmap_domain_pirq calls pci_disable_msi, which disables MSI
interrupts on the device. On the other hand map_domain_pirq doesn't
enable MSI, so the current update process of already enabled MSI
entries is wrong because MSI control bit will be disabled by
unmap_domain_pirq and not re-enabled by map_domain_pirq.

In order to fix this avoid unmapping the PIRQs and just update the
binding of the PIRQ. A new arch helper to do that is introduced.

Note that MSI-X is not affected because unmap_domain_pirq only
disables the MSI enable control bit for the MSI case, for MSI-X the
bit is left untouched by unmap_domain_pirq.

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
Acked-by: Jan Beulich <jbeulich@suse.com>
xen/arch/x86/hvm/vmsi.c
xen/drivers/vpci/msi.c
xen/include/xen/vpci.h

index acadc23f8da47affdd55838741251dc8dcc195fd..3001d5c488e60a45554b47e2b71b6aa3b4db6ec5 100644 (file)
@@ -699,6 +699,29 @@ static int vpci_msi_update(const struct pci_dev *pdev, uint32_t data,
     return 0;
 }
 
+int vpci_msi_arch_update(struct vpci_msi *msi, const struct pci_dev *pdev)
+{
+    int rc;
+
+    ASSERT(msi->arch.pirq != INVALID_PIRQ);
+
+    pcidevs_lock();
+    rc = vpci_msi_update(pdev, msi->data, msi->address, msi->vectors,
+                         msi->arch.pirq, msi->mask);
+    if ( rc )
+    {
+        spin_lock(&pdev->domain->event_lock);
+        unmap_domain_pirq(pdev->domain, msi->arch.pirq);
+        spin_unlock(&pdev->domain->event_lock);
+        pcidevs_unlock();
+        msi->arch.pirq = INVALID_PIRQ;
+        return rc;
+    }
+    pcidevs_unlock();
+
+    return 0;
+}
+
 static int vpci_msi_enable(const struct pci_dev *pdev, uint32_t data,
                            uint64_t address, unsigned int nr,
                            paddr_t table_base, uint32_t mask)
index ad26c38a92be2310a9f9ce7aaa2a1e0e4dd541a7..8f15ad7bf2788dac1a40f1a7f47bfa89d8e82068 100644 (file)
@@ -87,8 +87,7 @@ static void update_msi(const struct pci_dev *pdev, struct vpci_msi *msi)
     if ( !msi->enabled )
         return;
 
-    vpci_msi_arch_disable(msi, pdev);
-    if ( vpci_msi_arch_enable(msi, pdev, msi->vectors) )
+    if ( vpci_msi_arch_update(msi, pdev) )
         msi->enabled = false;
 }
 
index 72d2225a97d3a09f9946af61e3e99f23536bfebe..af2b8580ee793ac3f6934464549d261e42a383bc 100644 (file)
@@ -159,6 +159,8 @@ int __must_check vpci_msi_arch_enable(struct vpci_msi *msi,
                                       const struct pci_dev *pdev,
                                       unsigned int vectors);
 void vpci_msi_arch_disable(struct vpci_msi *msi, const struct pci_dev *pdev);
+int __must_check vpci_msi_arch_update(struct vpci_msi *msi,
+                                      const struct pci_dev *pdev);
 void vpci_msi_arch_init(struct vpci_msi *msi);
 void vpci_msi_arch_print(const struct vpci_msi *msi);