Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+14 −3
Original line number Diff line number Diff line
@@ -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;
	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)