x86: clean up MSI handlers and vectors
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 8 Oct 2019 21:08:17 +0000 (17:08 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 8 Oct 2019 21:11:11 +0000 (17:11 -0400)
With this change, drivers can deregister their IRQs, shut down their
devices, and reinitialize them.

Tested with MSI-X, but not MSI.  The IOAT device I have for testing is
MSI-X only.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/arch/x86/ioapic.c
kern/arch/x86/msi.c
kern/arch/x86/pci.h
kern/arch/x86/trap.c
kern/arch/x86/trap.h

index 70a71d2..3e0e481 100644 (file)
@@ -384,6 +384,12 @@ static void msi_route_irq(struct irq_handler *irq_h, int apic_vector, int dest)
        pci_msi_route(irq_h->dev_private, dest);
 }
 
+static void msi_cleanup_irq(struct irq_handler *irq_h)
+{
+       pci_msi_reset_vector(irq_h->dev_private);
+       put_irq_vector(irq_h->apic_vector);
+}
+
 static void msix_mask_irq(struct irq_handler *irq_h, int apic_vector)
 {
        pci_msix_mask_vector(irq_h->dev_private);
@@ -399,6 +405,13 @@ static void msix_route_irq(struct irq_handler *irq_h, int apic_vector, int dest)
        pci_msix_route_vector(irq_h->dev_private, dest);
 }
 
+static void msix_cleanup_irq(struct irq_handler *irq_h)
+{
+       pci_msix_reset_vector(irq_h->dev_private);
+       kfree(irq_h->dev_private);
+       put_irq_vector(irq_h->apic_vector);
+}
+
 static int msi_irq_enable(struct irq_handler *irq_h, struct pci_device *p)
 {
        unsigned int vno, lo, hi = 0;
@@ -427,6 +440,7 @@ static int msi_irq_enable(struct irq_handler *irq_h, struct pci_device *p)
                irq_h->mask = msi_mask_irq;
                irq_h->unmask = msi_unmask_irq;
                irq_h->route_irq = msi_route_irq;
+               irq_h->cleanup = msi_cleanup_irq;
                irq_h->type = "msi";
                printk("MSI irq: (%02x:%02x.%x): %s vector %d\n",
                           p->bus, p->dev, p->func, irq_h->name, vno);
@@ -437,6 +451,7 @@ static int msi_irq_enable(struct irq_handler *irq_h, struct pci_device *p)
        irq_h->mask = msix_mask_irq;
        irq_h->unmask = msix_unmask_irq;
        irq_h->route_irq = msix_route_irq;
+       irq_h->cleanup = msix_cleanup_irq;
        irq_h->type = "msi-x";
        printk("MSI-X irq: (%02x,%02x,%x): %s vector %d\n",
               p->bus, p->dev, p->func, irq_h->name, vno);
index 1cefe07..61c9832 100644 (file)
@@ -256,6 +256,12 @@ static uintptr_t msix_get_capbar_paddr(struct pci_device *p, int offset)
        return membar;
 }
 
+static void __msix_reset_entry(struct msix_entry *entry)
+{
+       __msix_mask_entry(entry);
+       write_mmreg32((uintptr_t)&entry->data, 0);
+}
+
 /* One time initialization of MSI-X for a PCI device.  -1 on error.  Otherwise,
  * the device will be ready to assign/route MSI-X entries/vectors.  All vectors
  * are masked, but the overall MSI-X function is unmasked.
@@ -315,10 +321,8 @@ static int __pci_msix_init(struct pci_device *p)
         * likewise, we need to 0 out the data, since we'll use the lower byte
         * later when determining if an msix vector is free or not. */
        entry = (struct msix_entry*)p->msix_tbl_vaddr;
-       for (int i = 0; i < p->msix_nr_vec; i++, entry++) {
-               __msix_mask_entry(entry);
-               write_mmreg32((uintptr_t)&entry->data, 0);
-       }
+       for (int i = 0; i < p->msix_nr_vec; i++, entry++)
+               __msix_reset_entry(entry);
        /* unmask the device, now that all the vectors are masked */
        f &= ~Msixmask;
        pcidev_write16(p, c + 2, f);
@@ -434,6 +438,19 @@ void pci_msi_route(struct pci_device *p, int dest)
        spin_unlock_irqsave(&p->lock);
 }
 
+void pci_msi_reset_vector(struct pci_device *p)
+{
+       /* Might be overly paranoid.  We're clearing out any old vector set in
+        * the device. */
+       spin_lock_irqsave(&p->lock);
+       p->msi_msg_addr_lo = 0;
+       p->msi_msg_addr_hi = 0;
+       p->msi_msg_data = 0;
+       __msi_set_addr_data(p, msicap(p));
+       p->msi_ready = false;
+       spin_unlock_irqsave(&p->lock);
+}
+
 void pci_msix_mask_vector(struct msix_irq_vector *linkage)
 {
        spin_lock_irqsave(&linkage->pcidev->lock);
@@ -457,3 +474,10 @@ void pci_msix_route_vector(struct msix_irq_vector *linkage, int dest)
        write_mmreg32((uintptr_t)&linkage->entry->addr_lo, linkage->addr_lo);
        spin_unlock_irqsave(&linkage->pcidev->lock);
 }
+
+void pci_msix_reset_vector(struct msix_irq_vector *linkage)
+{
+       spin_lock_irqsave(&linkage->pcidev->lock);
+       __msix_reset_entry(linkage->entry);
+       spin_unlock_irqsave(&linkage->pcidev->lock);
+}
index 2de8b49..6d3d85a 100644 (file)
@@ -276,9 +276,11 @@ struct msix_irq_vector *pci_msix_enable(struct pci_device *p, uint64_t vec);
 void pci_msi_mask(struct pci_device *p);
 void pci_msi_unmask(struct pci_device *p);
 void pci_msi_route(struct pci_device *p, int dest);
+void pci_msi_reset_vector(struct pci_device *p);
 void pci_msix_mask_vector(struct msix_irq_vector *linkage);
 void pci_msix_unmask_vector(struct msix_irq_vector *linkage);
 void pci_msix_route_vector(struct msix_irq_vector *linkage, int dest);
+void pci_msix_reset_vector(struct msix_irq_vector *linkage);
 
 /* TODO: this is quite the Hacke */
 #define explode_tbdf(tbdf) {pcidev.bus = tbdf >> 16;\
index daa7588..b630aba 100644 (file)
@@ -840,7 +840,13 @@ int deregister_irq(int vector, uint32_t tbdf)
                warn("No IRQ V: %d TBDF: %x to unregister!", vector, tbdf);
                return -1;
        }
+       /* Ideally, the driver should have told the device to not fire the IRQ
+        * anymore.  If they do, we may get a warn_once.  This could be on
+        * another core, etc. */
+       irq_h->mask(irq_h, irq_h->apic_vector);
        synchronize_rcu();
+       if (irq_h->cleanup)
+               irq_h->cleanup(irq_h);
        kfree(irq_h);
        return 0;
 }
index 051b9c6..71ba857 100644 (file)
@@ -140,6 +140,7 @@ struct irq_handler {
        void (*mask)(struct irq_handler *irq_h, int vec);
        void (*unmask)(struct irq_handler *irq_h, int vec);
        void (*route_irq)(struct irq_handler *irq_h, int vec, int dest);
+       void (*cleanup)(struct irq_handler *irq_h);
 
        int tbdf;
        int dev_irq;