mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-09 20:32:04 +09:00
pci/hotplug/pnv-php: Disable MSI and PCI device properly
commit49f4b08e61upstream. 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:360aebd85a("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>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
bc5338a4fd
commit
1fb738a3dc
@@ -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)
|
|||||||
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)
|
|||||||
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);
|
||||||
}
|
}
|
||||||
@@ -759,7 +765,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -772,7 +778,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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user