PCI: add routines for debugging and handling lost interrupts

We're getting a lot of storage drivers blamed for interrupt misrouting
issues. This patch provides a standard way of reporting the problem
... and, if possible, correcting it.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>

authored by James Bottomley and committed by Jesse Barnes 388c8c16 18b341b7

+69 -1
+2 -1
drivers/pci/Makefile
··· 3 # 4 5 obj-y += access.o bus.o probe.o remove.o pci.o quirks.o slot.o \ 6 - pci-driver.o search.o pci-sysfs.o rom.o setup-res.o 7 obj-$(CONFIG_PROC_FS) += proc.o 8 9 # Build PCI Express stuff if needed
··· 3 # 4 5 obj-y += access.o bus.o probe.o remove.o pci.o quirks.o slot.o \ 6 + pci-driver.o search.o pci-sysfs.o rom.o setup-res.o \ 7 + irq.o 8 obj-$(CONFIG_PROC_FS) += proc.o 9 10 # Build PCI Express stuff if needed
+60
drivers/pci/irq.c
···
··· 1 + /* 2 + * PCI IRQ failure handing code 3 + * 4 + * Copyright (c) 2008 James Bottomley <James.Bottomley@HansenPartnership.com> 5 + */ 6 + 7 + #include <linux/acpi.h> 8 + #include <linux/device.h> 9 + #include <linux/kernel.h> 10 + #include <linux/pci.h> 11 + 12 + static void pci_note_irq_problem(struct pci_dev *pdev, const char *reason) 13 + { 14 + struct pci_dev *parent = to_pci_dev(pdev->dev.parent); 15 + 16 + dev_printk(KERN_ERR, &pdev->dev, 17 + "Potentially misrouted IRQ (Bridge %s %04x:%04x)\n", 18 + parent->dev.bus_id, parent->vendor, parent->device); 19 + dev_printk(KERN_ERR, &pdev->dev, "%s\n", reason); 20 + dev_printk(KERN_ERR, &pdev->dev, "Please report to linux-kernel@vger.kernel.org\n"); 21 + WARN_ON(1); 22 + } 23 + 24 + /** 25 + * pci_lost_interrupt - reports a lost PCI interrupt 26 + * @pdev: device whose interrupt is lost 27 + * 28 + * The primary function of this routine is to report a lost interrupt 29 + * in a standard way which users can recognise (instead of blaming the 30 + * driver). 31 + * 32 + * Returns: 33 + * a suggestion for fixing it (although the driver is not required to 34 + * act on this). 35 + */ 36 + enum pci_lost_interrupt_reason pci_lost_interrupt(struct pci_dev *pdev) 37 + { 38 + if (pdev->msi_enabled || pdev->msix_enabled) { 39 + enum pci_lost_interrupt_reason ret; 40 + 41 + if (pdev->msix_enabled) { 42 + pci_note_irq_problem(pdev, "MSIX routing failure"); 43 + ret = PCI_LOST_IRQ_DISABLE_MSIX; 44 + } else { 45 + pci_note_irq_problem(pdev, "MSI routing failure"); 46 + ret = PCI_LOST_IRQ_DISABLE_MSI; 47 + } 48 + return ret; 49 + } 50 + #ifdef CONFIG_ACPI 51 + if (!(acpi_disabled || acpi_noirq)) { 52 + pci_note_irq_problem(pdev, "Potential ACPI misrouting please reboot with acpi=noirq"); 53 + /* currently no way to fix acpi on the fly */ 54 + return PCI_LOST_IRQ_DISABLE_ACPI; 55 + } 56 + #endif 57 + pci_note_irq_problem(pdev, "unknown cause (not MSI or ACPI)"); 58 + return PCI_LOST_IRQ_NO_INFORMATION; 59 + } 60 + EXPORT_SYMBOL(pci_lost_interrupt);
+7
include/linux/pci.h
··· 546 unsigned int devfn); 547 #endif /* CONFIG_PCI_LEGACY */ 548 549 int pci_find_capability(struct pci_dev *dev, int cap); 550 int pci_find_next_capability(struct pci_dev *dev, u8 pos, int cap); 551 int pci_find_ext_capability(struct pci_dev *dev, int cap);
··· 546 unsigned int devfn); 547 #endif /* CONFIG_PCI_LEGACY */ 548 549 + enum pci_lost_interrupt_reason { 550 + PCI_LOST_IRQ_NO_INFORMATION = 0, 551 + PCI_LOST_IRQ_DISABLE_MSI, 552 + PCI_LOST_IRQ_DISABLE_MSIX, 553 + PCI_LOST_IRQ_DISABLE_ACPI, 554 + }; 555 + enum pci_lost_interrupt_reason pci_lost_interrupt(struct pci_dev *dev); 556 int pci_find_capability(struct pci_dev *dev, int cap); 557 int pci_find_next_capability(struct pci_dev *dev, u8 pos, int cap); 558 int pci_find_ext_capability(struct pci_dev *dev, int cap);