Skip to content

Commit

Permalink
PCI: acpiphp: cleanup notify handler on all root bridges
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Alex Chiang authored and Jesse Barnes committed Jul 2, 2008
1 parent 99cb233 commit a13307c
Showing 1 changed file with 14 additions and 3 deletions.
17 changes: 14 additions & 3 deletions drivers/pci/hotplug/acpiphp_glue.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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)
Expand Down

0 comments on commit a13307c

Please sign in to comment.