| From 49f4b08e61547a5ccd2db551d994c4503efe5666 Mon Sep 17 00:00:00 2001 |
| From: Gavin Shan <gwshan@linux.vnet.ibm.com> |
| Date: Thu, 16 Feb 2017 10:22:34 +1100 |
| Subject: pci/hotplug/pnv-php: Disable MSI and PCI device properly |
| |
| From: Gavin Shan <gwshan@linux.vnet.ibm.com> |
| |
| commit 49f4b08e61547a5ccd2db551d994c4503efe5666 upstream. |
| |
| pnv_php_disable_irq() can be called in two paths: Bailing path in |
| pnv_php_enable_irq() or releasing slot. The MSI (or MSIx) interrupts |
| is disabled unconditionally in pnv_php_disable_irq(). It's wrong |
| because that might be enabled by drivers other than pnv-php. |
| |
| This disables MSI (or MSIx) interrupts and the PCI device only if |
| it was enabled by pnv-php. In the error path of pnv_php_enable_irq(), |
| we rely on the newly added parameter @disable_device. In the path |
| of releasing slot, @pnv_php->irq is checked. |
| |
| Fixes: 360aebd85a4c ("drivers/pci/hotplug: Support surprise hotplug in powernv driver") |
| Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com> |
| Reviewed-by: Andrew Donnellan <andrew.donnellan@au1.ibm.com> |
| Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/pci/hotplug/pnv_php.c | 22 ++++++++++++++-------- |
| 1 file changed, 14 insertions(+), 8 deletions(-) |
| |
| --- a/drivers/pci/hotplug/pnv_php.c |
| +++ b/drivers/pci/hotplug/pnv_php.c |
| @@ -35,9 +35,11 @@ static void pnv_php_register(struct devi |
| static void pnv_php_unregister_one(struct device_node *dn); |
| static void pnv_php_unregister(struct device_node *dn); |
| |
| -static void pnv_php_disable_irq(struct pnv_php_slot *php_slot) |
| +static void pnv_php_disable_irq(struct pnv_php_slot *php_slot, |
| + bool disable_device) |
| { |
| struct pci_dev *pdev = php_slot->pdev; |
| + int irq = php_slot->irq; |
| u16 ctrl; |
| |
| if (php_slot->irq > 0) { |
| @@ -56,10 +58,14 @@ static void pnv_php_disable_irq(struct p |
| php_slot->wq = NULL; |
| } |
| |
| - if (pdev->msix_enabled) |
| - pci_disable_msix(pdev); |
| - else if (pdev->msi_enabled) |
| - pci_disable_msi(pdev); |
| + if (disable_device || irq > 0) { |
| + if (pdev->msix_enabled) |
| + pci_disable_msix(pdev); |
| + else if (pdev->msi_enabled) |
| + pci_disable_msi(pdev); |
| + |
| + pci_disable_device(pdev); |
| + } |
| } |
| |
| static void pnv_php_free_slot(struct kref *kref) |
| @@ -68,7 +74,7 @@ static void pnv_php_free_slot(struct kre |
| struct pnv_php_slot, kref); |
| |
| WARN_ON(!list_empty(&php_slot->children)); |
| - pnv_php_disable_irq(php_slot); |
| + pnv_php_disable_irq(php_slot, false); |
| kfree(php_slot->name); |
| kfree(php_slot); |
| } |
| @@ -759,7 +765,7 @@ static void pnv_php_init_irq(struct pnv_ |
| php_slot->wq = alloc_workqueue("pciehp-%s", 0, 0, php_slot->name); |
| if (!php_slot->wq) { |
| dev_warn(&pdev->dev, "Cannot alloc workqueue\n"); |
| - pnv_php_disable_irq(php_slot); |
| + pnv_php_disable_irq(php_slot, true); |
| return; |
| } |
| |
| @@ -772,7 +778,7 @@ static void pnv_php_init_irq(struct pnv_ |
| ret = request_irq(irq, pnv_php_interrupt, IRQF_SHARED, |
| php_slot->name, php_slot); |
| if (ret) { |
| - pnv_php_disable_irq(php_slot); |
| + pnv_php_disable_irq(php_slot, true); |
| dev_warn(&pdev->dev, "Error %d enabling IRQ %d\n", ret, irq); |
| return; |
| } |