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;