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>
This commit is contained in:
Родитель
99cb233d60
Коммит
a13307cef8
|
@ -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,
|
||||
cleanup_p2p_bridge, NULL, NULL);
|
||||
|
||||
if (!(bridge = acpiphp_handle_to_bridge(handle)))
|
||||
return AE_OK;
|
||||
cleanup_bridge(bridge);
|
||||
bridge = acpiphp_handle_to_bridge(handle);
|
||||
if (bridge)
|
||||
cleanup_bridge(bridge);
|
||||
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
|
@ -715,9 +716,19 @@ static void remove_bridge(acpi_handle handle)
|
|||
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
|
||||
(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);
|
||||
if (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)
|
||||
|
|
Загрузка…
Ссылка в новой задаче