From 032de8e2fe3c0eec5fb0ffe4d38aa602dad397dc Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Wed, 18 Apr 2007 19:39:22 +1000 Subject: [PATCH] MSI: Give archs the option to free all MSI/Xs at once. This patch introduces an optional function, arch_teardown_msi_irqs(), which gives an arch the opportunity to do per-device teardown for MSI/X. If that's not required, the default version simply calls arch_teardown_msi_irq() for each msi irq required. arch_teardown_msi_irqs() is simply passed a pdev, attached to the pdev is a list of msi_descs, it is up to the arch to free the irq associated with each of these as appropriate. For archs that _don't_ implement arch_teardown_msi_irqs(), all msi_descs with irq == 0 are considered unallocated, and the arch teardown routine is not called on them. Signed-off-by: Michael Ellerman Signed-off-by: Greg Kroah-Hartman --- drivers/pci/msi.c | 75 +++++++++++++++++++++++---------------------- include/linux/msi.h | 1 + 2 files changed, 40 insertions(+), 36 deletions(-) diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index c71e8e4c7168..9e1321d0d5e6 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -207,7 +207,7 @@ void unmask_msi_irq(unsigned int irq) msix_flush_writes(irq); } -static int msi_free_irq(struct pci_dev* dev, int irq); +static int msi_free_irqs(struct pci_dev* dev); static struct msi_desc* alloc_msi_entry(void) @@ -339,8 +339,7 @@ static int msi_capability_init(struct pci_dev *dev) /* Configure MSI capability structure */ ret = arch_setup_msi_irqs(dev, 1, PCI_CAP_ID_MSI); if (ret) { - list_del(&entry->list); - kfree(entry); + msi_free_irqs(dev); return ret; } @@ -415,10 +414,11 @@ static int msix_capability_init(struct pci_dev *dev, list_for_each_entry(entry, &dev->msi_list, list) { if (entry->irq != 0) { avail++; - msi_free_irq(dev, entry->irq); } } + msi_free_irqs(dev); + /* If we had some success report the number of irqs * we succeeded in setting up. */ @@ -539,39 +539,33 @@ void pci_disable_msi(struct pci_dev* dev) } default_irq = entry->msi_attrib.default_irq; - msi_free_irq(dev, entry->irq); + msi_free_irqs(dev); /* Restore dev->irq to its default pin-assertion irq */ dev->irq = default_irq; } EXPORT_SYMBOL(pci_disable_msi); -static int msi_free_irq(struct pci_dev* dev, int irq) +static int msi_free_irqs(struct pci_dev* dev) { - struct msi_desc *entry; - int entry_nr, type; - void __iomem *base; - - BUG_ON(irq_has_action(irq)); + struct msi_desc *entry, *tmp; - entry = get_irq_msi(irq); - if (!entry || entry->dev != dev) { - return -EINVAL; - } - type = entry->msi_attrib.type; - entry_nr = entry->msi_attrib.entry_nr; - base = entry->mask_base; - list_del(&entry->list); + list_for_each_entry(entry, &dev->msi_list, list) + BUG_ON(irq_has_action(entry->irq)); - arch_teardown_msi_irq(irq); - kfree(entry); + arch_teardown_msi_irqs(dev); - if (type == PCI_CAP_ID_MSIX) { - writel(1, base + entry_nr * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET); + list_for_each_entry_safe(entry, tmp, &dev->msi_list, list) { + if (entry->msi_attrib.type == PCI_CAP_ID_MSIX) { + if (list_is_last(&entry->list, &dev->msi_list)) + iounmap(entry->mask_base); - if (list_empty(&dev->msi_list)) - iounmap(base); + writel(1, entry->mask_base + entry->msi_attrib.entry_nr + * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET); + } + list_del(&entry->list); + kfree(entry); } return 0; @@ -636,10 +630,7 @@ EXPORT_SYMBOL(pci_enable_msix); static void msix_free_all_irqs(struct pci_dev *dev) { - struct msi_desc *entry; - - list_for_each_entry(entry, &dev->msi_list, list) - msi_free_irq(dev, entry->irq); + msi_free_irqs(dev); } void pci_disable_msix(struct pci_dev* dev) @@ -669,12 +660,8 @@ void msi_remove_pci_irq_vectors(struct pci_dev* dev) if (!pci_msi_enable || !dev) return; - if (dev->msi_enabled) { - struct msi_desc *entry; - BUG_ON(list_empty(&dev->msi_list)); - entry = list_entry(dev->msi_list.next, struct msi_desc, list); - msi_free_irq(dev, entry->irq); - } + if (dev->msi_enabled) + msi_free_irqs(dev); if (dev->msix_enabled) msix_free_all_irqs(dev); @@ -719,3 +706,19 @@ arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) return 0; } + +void __attribute__ ((weak)) arch_teardown_msi_irq(unsigned int irq) +{ + return; +} + +void __attribute__ ((weak)) +arch_teardown_msi_irqs(struct pci_dev *dev) +{ + struct msi_desc *entry; + + list_for_each_entry(entry, &dev->msi_list, list) { + if (entry->irq != 0) + arch_teardown_msi_irq(entry->irq); + } +} diff --git a/include/linux/msi.h b/include/linux/msi.h index 494627ae021f..94bb46d82efd 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -42,6 +42,7 @@ struct msi_desc { int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc); void arch_teardown_msi_irq(unsigned int irq); extern int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type); +extern void arch_teardown_msi_irqs(struct pci_dev *dev); extern int arch_msi_check_device(struct pci_dev* dev, int nvec, int type); -- 2.30.2