x86: clean up MSI handlers and vectors 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>
diff --git a/kern/arch/x86/ioapic.c b/kern/arch/x86/ioapic.c index 70a71d2..3e0e481 100644 --- a/kern/arch/x86/ioapic.c +++ b/kern/arch/x86/ioapic.c
@@ -384,6 +384,12 @@ 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 @@ 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 @@ 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 @@ 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);
diff --git a/kern/arch/x86/msi.c b/kern/arch/x86/msi.c index 1cefe07..61c9832 100644 --- a/kern/arch/x86/msi.c +++ b/kern/arch/x86/msi.c
@@ -256,6 +256,12 @@ 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 @@ * 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 @@ 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 @@ 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); +}
diff --git a/kern/arch/x86/pci.h b/kern/arch/x86/pci.h index 2de8b49..6d3d85a 100644 --- a/kern/arch/x86/pci.h +++ b/kern/arch/x86/pci.h
@@ -276,9 +276,11 @@ 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;\
diff --git a/kern/arch/x86/trap.c b/kern/arch/x86/trap.c index daa7588..b630aba 100644 --- a/kern/arch/x86/trap.c +++ b/kern/arch/x86/trap.c
@@ -840,7 +840,13 @@ 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; }
diff --git a/kern/arch/x86/trap.h b/kern/arch/x86/trap.h index 051b9c6..71ba857 100644 --- a/kern/arch/x86/trap.h +++ b/kern/arch/x86/trap.h
@@ -140,6 +140,7 @@ 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;