diff options
| author | Alex Chiang <achiang@hp.com> | 2008-07-01 22:02:23 -0400 |
|---|---|---|
| committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2008-07-02 14:27:30 -0400 |
| commit | a13307cef8bf51990ef1d525b1cbdcc2cfe07e2a (patch) | |
| tree | 3098b0057aa09f53c9ecd485fc147f135eecfc33 /drivers | |
| parent | 99cb233d60cbe644203f19938c729ea2bb004d70 (diff) | |
PCI: acpiphp: cleanup notify handler on all root bridges
During the development of the physical PCI slot patch series, Gary Hade
kept on reporting strange oopses due to interactions between pci_slot
and acpiphp.
http://lkml.org/lkml/2007/11/28/319
find_root_bridges() unconditionally installs
handle_hotplug_event_bridge() as an ACPI_SYSTEM_NOTIFY handler for all
root bridges.
However, during module cleanup, remove_bridge() will only remove the
notify handler iff the root bridge had a hot-pluggable slot directly
underneath. That is:
root bridge -> hotplug slot
But, if the topology looks like either of the following:
root bridge -> non-hotplug slot
root bridge -> p2p bridge -> hotplug slot
Then we currently do not remove the notify handler from that root
bridge.
This can cause a kernel oops if we modprobe acpiphp later and it gets
loaded somewhere else in memory. If the root bridge then receives a
hotplug event, it will then attempt to call a stale, non-existent notify
handler and we blow up.
Much thanks goes to Gary Hade for his persistent debugging efforts.
Signed-off-by: Alex Chiang <achiang@hp.com>
Signed-off-by: Gary Hade <garyhade@us.ibm.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/pci/hotplug/acpiphp_glue.c | 17 |
1 files changed, 14 insertions, 3 deletions
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 648596d469f6..91156f85a926 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c | |||
| @@ -700,9 +700,10 @@ cleanup_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv) | |||
| 700 | acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, | 700 | acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, |
| 701 | cleanup_p2p_bridge, NULL, NULL); | 701 | cleanup_p2p_bridge, NULL, NULL); |
| 702 | 702 | ||
| 703 | if (!(bridge = acpiphp_handle_to_bridge(handle))) | 703 | bridge = acpiphp_handle_to_bridge(handle); |
| 704 | return AE_OK; | 704 | if (bridge) |
| 705 | cleanup_bridge(bridge); | 705 | cleanup_bridge(bridge); |
| 706 | |||
| 706 | return AE_OK; | 707 | return AE_OK; |
| 707 | } | 708 | } |
| 708 | 709 | ||
| @@ -715,9 +716,19 @@ static void remove_bridge(acpi_handle handle) | |||
| 715 | acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, | 716 | acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, |
| 716 | (u32)1, cleanup_p2p_bridge, NULL, NULL); | 717 | (u32)1, cleanup_p2p_bridge, NULL, NULL); |
| 717 | 718 | ||
| 719 | /* | ||
| 720 | * On root bridges with hotplug slots directly underneath (ie, | ||
| 721 | * no p2p bridge inbetween), we call cleanup_bridge(). | ||
| 722 | * | ||
| 723 | * The else clause cleans up root bridges that either had no | ||
| 724 | * hotplug slots at all, or had a p2p bridge underneath. | ||
| 725 | */ | ||
| 718 | bridge = acpiphp_handle_to_bridge(handle); | 726 | bridge = acpiphp_handle_to_bridge(handle); |
| 719 | if (bridge) | 727 | if (bridge) |
| 720 | cleanup_bridge(bridge); | 728 | cleanup_bridge(bridge); |
| 729 | else | ||
| 730 | acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, | ||
| 731 | handle_hotplug_event_bridge); | ||
| 721 | } | 732 | } |
| 722 | 733 | ||
| 723 | static struct pci_dev * get_apic_pci_info(acpi_handle handle) | 734 | static struct pci_dev * get_apic_pci_info(acpi_handle handle) |
