[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <1844787.IDye8ayOzv@vostro.rjw.lan>
Date: Tue, 04 Feb 2014 00:18:57 +0100
From: "Rafael J. Wysocki" <rjw@...ysocki.net>
To: ACPI Devel Maling List <linux-acpi@...r.kernel.org>
Cc: Mika Westerberg <mika.westerberg@...ux.intel.com>,
Bjorn Helgaas <bhelgaas@...gle.com>,
Aaron Lu <aaron.lu@...el.com>,
Linux Kernel Mailing List <linux-kernel@...r.kernel.org>,
Linux PCI <linux-pci@...r.kernel.org>
Subject: [PATCH 5/24][New] ACPI / hotplug / PCI: Fix bridge removal race vs dock events
From: Rafael J. Wysocki <rafael.j.wysocki@...el.com>
If a PCI bridge with an ACPIPHP context attached is removed via
sysfs, the code path executed as a result is the following:
pci_stop_and_remove_bus_device_locked
pci_remove_bus
pcibios_remove_bus
acpi_pci_remove_bus
acpiphp_remove_slots
cleanup_bridge
unregister_hotplug_dock_device (drops dock references to the bridge)
put_bridge
free_bridge
acpiphp_put_context (for each child, under context lock)
kfree (context)
Now, if a dock event affecting one of the bridge's child devices
occurs (roughly at the same time), it will lead to the following code
path:
acpi_dock_deferred_cb
dock_notify
handle_eject_request
hot_remove_dock_devices
dock_hotplug_event
hotplug_event (dereferences context)
That may lead to a kernel crash in hotplug_event() if it is executed
after the last kfree() in the bridge removal code path.
To prevent that from happening, add a wrapper around hotplug_event()
called dock_event() and point the .handler pointer in acpiphp_dock_ops
to it. Make that wrapper retrieve the device's ACPIPHP context using
acpiphp_get_context() (instead of taking it from the data argument)
under acpiphp_context_lock and check if the parent bridge's
is_going_away flag is set. If that flag is set, it will return
immediately and if it is not set it will grab a reference to the
device's parent bridge before executing hotplug_event().
Then, in the above scenario, the reference to the parent bridge
held by dock_event() will prevent free_bridge() from being executed
for it until hotplug_event() returns.
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@...el.com>
---
drivers/pci/hotplug/acpiphp_glue.c | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
Index: linux-pm/drivers/pci/hotplug/acpiphp_glue.c
===================================================================
--- linux-pm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-pm/drivers/pci/hotplug/acpiphp_glue.c
@@ -210,10 +210,29 @@ static void post_dock_fixups(acpi_handle
}
}
+static void dock_event(acpi_handle handle, u32 type, void *data)
+{
+ struct acpiphp_context *context;
+
+ mutex_lock(&acpiphp_context_lock);
+ context = acpiphp_get_context(handle);
+ if (!context || WARN_ON(context->handle != handle)
+ || context->func.parent->is_going_away) {
+ mutex_unlock(&acpiphp_context_lock);
+ return;
+ }
+ get_bridge(context->func.parent);
+ acpiphp_put_context(context);
+ mutex_unlock(&acpiphp_context_lock);
+
+ hotplug_event(handle, type, data);
+
+ put_bridge(context->func.parent);
+}
static const struct acpi_dock_ops acpiphp_dock_ops = {
.fixup = post_dock_fixups,
- .handler = hotplug_event,
+ .handler = dock_event,
};
/* Check whether the PCI device is managed by native PCIe hotplug driver */
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists