Commit 49f4b08e authored by Gavin Shan's avatar Gavin Shan Committed by Michael Ellerman

pci/hotplug/pnv-php: Disable MSI and PCI device properly

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.

Cc: <stable@vger.kernel.org> # v4.9+
Fixes: 360aebd8 ("drivers/pci/hotplug: Support surprise hotplug in powernv driver")
Signed-off-by: default avatarGavin Shan <gwshan@linux.vnet.ibm.com>
Reviewed-by: default avatarAndrew Donnellan <andrew.donnellan@au1.ibm.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent 303529d6
...@@ -35,9 +35,11 @@ static void pnv_php_register(struct device_node *dn); ...@@ -35,9 +35,11 @@ static void pnv_php_register(struct device_node *dn);
static void pnv_php_unregister_one(struct device_node *dn); static void pnv_php_unregister_one(struct device_node *dn);
static void pnv_php_unregister(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; struct pci_dev *pdev = php_slot->pdev;
int irq = php_slot->irq;
u16 ctrl; u16 ctrl;
if (php_slot->irq > 0) { if (php_slot->irq > 0) {
...@@ -56,10 +58,14 @@ static void pnv_php_disable_irq(struct pnv_php_slot *php_slot) ...@@ -56,10 +58,14 @@ static void pnv_php_disable_irq(struct pnv_php_slot *php_slot)
php_slot->wq = NULL; php_slot->wq = NULL;
} }
if (pdev->msix_enabled) if (disable_device || irq > 0) {
pci_disable_msix(pdev); if (pdev->msix_enabled)
else if (pdev->msi_enabled) pci_disable_msix(pdev);
pci_disable_msi(pdev); else if (pdev->msi_enabled)
pci_disable_msi(pdev);
pci_disable_device(pdev);
}
} }
static void pnv_php_free_slot(struct kref *kref) static void pnv_php_free_slot(struct kref *kref)
...@@ -68,7 +74,7 @@ static void pnv_php_free_slot(struct kref *kref) ...@@ -68,7 +74,7 @@ static void pnv_php_free_slot(struct kref *kref)
struct pnv_php_slot, kref); struct pnv_php_slot, kref);
WARN_ON(!list_empty(&php_slot->children)); 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->name);
kfree(php_slot); kfree(php_slot);
} }
...@@ -777,7 +783,7 @@ static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq) ...@@ -777,7 +783,7 @@ static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq)
php_slot->wq = alloc_workqueue("pciehp-%s", 0, 0, php_slot->name); php_slot->wq = alloc_workqueue("pciehp-%s", 0, 0, php_slot->name);
if (!php_slot->wq) { if (!php_slot->wq) {
dev_warn(&pdev->dev, "Cannot alloc workqueue\n"); dev_warn(&pdev->dev, "Cannot alloc workqueue\n");
pnv_php_disable_irq(php_slot); pnv_php_disable_irq(php_slot, true);
return; return;
} }
...@@ -799,7 +805,7 @@ static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq) ...@@ -799,7 +805,7 @@ static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq)
ret = request_irq(irq, pnv_php_interrupt, IRQF_SHARED, ret = request_irq(irq, pnv_php_interrupt, IRQF_SHARED,
php_slot->name, php_slot); php_slot->name, php_slot);
if (ret) { 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); dev_warn(&pdev->dev, "Error %d enabling IRQ %d\n", ret, irq);
return; return;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment