diff options
author | Alex Chiang <achiang@hp.com> | 2009-03-30 12:50:14 -0400 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2009-04-06 14:30:26 -0400 |
commit | 5d4a4b25ddc3e864d3a562c024bebdc922118854 (patch) | |
tree | 9f0c9cdf4c14487517be83e82596a83616d905af | |
parent | c2ac7cdc6707a8a3fc5cdaebb65cb724ba8ecdef (diff) |
PCI Hotplug: acpiphp: grab refcount on p2p subordinate bus
If a logical hot unplug (remove) is performed on a bridge claimed
by acpiphp and then acpiphp is unloaded, we will encounter an oops.
This is because acpiphp will access the bridge's subordinate bus,
which was released by the user's prior hot unplug.
The solution is to grab a reference on the subordinate PCI bus.
This will prevent the bus from release until acpiphp is unloaded.
Reviewed-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Tested-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Reported-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Signed-off-by: Alex Chiang <achiang@hp.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
-rw-r--r-- | drivers/pci/hotplug/acpiphp_glue.c | 14 |
1 files changed, 14 insertions, 0 deletions
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 803d9ddd6e75..a33794d9e0dc 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c | |||
@@ -38,6 +38,8 @@ | |||
38 | * - The one in acpiphp_bridge has its refcount elevated by pci_get_slot() | 38 | * - The one in acpiphp_bridge has its refcount elevated by pci_get_slot() |
39 | * when the bridge is scanned and it loses a refcount when the bridge | 39 | * when the bridge is scanned and it loses a refcount when the bridge |
40 | * is removed. | 40 | * is removed. |
41 | * - When a P2P bridge is present, we elevate the refcount on the subordinate | ||
42 | * bus. It loses the refcount when the the driver unloads. | ||
41 | */ | 43 | */ |
42 | 44 | ||
43 | #include <linux/init.h> | 45 | #include <linux/init.h> |
@@ -440,6 +442,12 @@ static void add_p2p_bridge(acpi_handle *handle, struct pci_dev *pci_dev) | |||
440 | goto err; | 442 | goto err; |
441 | } | 443 | } |
442 | 444 | ||
445 | /* | ||
446 | * Grab a ref to the subordinate PCI bus in case the bus is | ||
447 | * removed via PCI core logical hotplug. The ref pins the bus | ||
448 | * (which we access during module unload). | ||
449 | */ | ||
450 | get_device(&bridge->pci_bus->dev); | ||
443 | spin_lock_init(&bridge->res_lock); | 451 | spin_lock_init(&bridge->res_lock); |
444 | 452 | ||
445 | init_bridge_misc(bridge); | 453 | init_bridge_misc(bridge); |
@@ -619,6 +627,12 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) | |||
619 | slot = next; | 627 | slot = next; |
620 | } | 628 | } |
621 | 629 | ||
630 | /* | ||
631 | * Only P2P bridges have a pci_dev | ||
632 | */ | ||
633 | if (bridge->pci_dev) | ||
634 | put_device(&bridge->pci_bus->dev); | ||
635 | |||
622 | pci_dev_put(bridge->pci_dev); | 636 | pci_dev_put(bridge->pci_dev); |
623 | list_del(&bridge->list); | 637 | list_del(&bridge->list); |
624 | kfree(bridge); | 638 | kfree(bridge); |