aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Chiang <achiang@hp.com>2008-07-01 22:02:23 -0400
committerJesse Barnes <jbarnes@virtuousgeek.org>2008-07-02 14:27:30 -0400
commita13307cef8bf51990ef1d525b1cbdcc2cfe07e2a (patch)
tree3098b0057aa09f53c9ecd485fc147f135eecfc33
parent99cb233d60cbe644203f19938c729ea2bb004d70 (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>
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c17
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
723static struct pci_dev * get_apic_pci_info(acpi_handle handle) 734static struct pci_dev * get_apic_pci_info(acpi_handle handle)