From: James Paradis <jparadis@redhat.com> Date: Tue, 10 Feb 2009 16:56:51 -0500 Subject: [pci] fix MSI descriptor leak during hot-unplug Message-id: 764530118.5715091234303011132.JavaMail.root@zmail01.collab.prod.int.phx2.redhat.com O-Subject: [RHEL5.4 PATCH] Fix PCI MSI descriptor leak Bugzilla: 484943 RH-Acked-by: Prarit Bhargava <prarit@redhat.com> https://bugzilla.redhat.com/show_bug.cgi?id=484943 This patch fixes a bug where the kernel can leak PCI MSI descriptors during a surprise hot-unplug. This happens because pci_disable_msi and pci_disable_msix read device config space before cleaning up, and bail out if it appears as though MSI capability not present. During a surprise unplug PCI config space for the device no longer exists, and we're parsing garbage. This fix is a back port of an upstream patch between 2.6.20 and 2.6.21 (866a8c87c4e51046602387953bbef76992107bcb for those playing along at home...) --jim diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 96bec55..0a497e5 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -984,16 +984,15 @@ void pci_disable_msi(struct pci_dev* dev) return; if (!dev) return; - - pos = pci_find_capability(dev, PCI_CAP_ID_MSI); - if (!pos) + if (!dev->msi_enabled) return; - pci_read_config_word(dev, msi_control_reg(pos), &control); - if (!(control & PCI_MSI_FLAGS_ENABLE)) - return; - - disable_msi_mode(dev, pos, PCI_CAP_ID_MSI); + pos = pci_find_capability(dev, PCI_CAP_ID_MSI); + if (pos) { + pci_read_config_word(dev, msi_control_reg(pos), &control); + if (control & PCI_MSI_FLAGS_ENABLE) + disable_msi_mode(dev, pos, PCI_CAP_ID_MSI); + } spin_lock_irqsave(&msi_lock, flags); entry = msi_desc[dev->irq]; @@ -1160,16 +1159,15 @@ void pci_disable_msix(struct pci_dev* dev) return; if (!dev) return; - - pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); - if (!pos) + if (!dev->msix_enabled) return; - pci_read_config_word(dev, msi_control_reg(pos), &control); - if (!(control & PCI_MSIX_FLAGS_ENABLE)) - return; - - disable_msi_mode(dev, pos, PCI_CAP_ID_MSIX); + pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); + if (pos) { + pci_read_config_word(dev, msi_control_reg(pos), &control); + if (control & PCI_MSIX_FLAGS_ENABLE) + disable_msi_mode(dev, pos, PCI_CAP_ID_MSIX); + } temp = dev->irq; if (!msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) {