Commit a13307ce authored by Alex Chiang's avatar Alex Chiang Committed by Jesse Barnes

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: default avatarAlex Chiang <achiang@hp.com>
Signed-off-by: default avatarGary Hade <garyhade@us.ibm.com>
Signed-off-by: default avatarJesse Barnes <jbarnes@virtuousgeek.org>
parent 99cb233d
...@@ -700,9 +700,10 @@ cleanup_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv) ...@@ -700,9 +700,10 @@ cleanup_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
cleanup_p2p_bridge, NULL, NULL); cleanup_p2p_bridge, NULL, NULL);
if (!(bridge = acpiphp_handle_to_bridge(handle))) bridge = acpiphp_handle_to_bridge(handle);
return AE_OK; if (bridge)
cleanup_bridge(bridge); cleanup_bridge(bridge);
return AE_OK; return AE_OK;
} }
...@@ -715,9 +716,19 @@ static void remove_bridge(acpi_handle handle) ...@@ -715,9 +716,19 @@ static void remove_bridge(acpi_handle handle)
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
(u32)1, cleanup_p2p_bridge, NULL, NULL); (u32)1, cleanup_p2p_bridge, NULL, NULL);
/*
* On root bridges with hotplug slots directly underneath (ie,
* no p2p bridge inbetween), we call cleanup_bridge().
*
* The else clause cleans up root bridges that either had no
* hotplug slots at all, or had a p2p bridge underneath.
*/
bridge = acpiphp_handle_to_bridge(handle); bridge = acpiphp_handle_to_bridge(handle);
if (bridge) if (bridge)
cleanup_bridge(bridge); cleanup_bridge(bridge);
else
acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
handle_hotplug_event_bridge);
} }
static struct pci_dev * get_apic_pci_info(acpi_handle handle) static struct pci_dev * get_apic_pci_info(acpi_handle handle)
......
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