===================================================================
@@ -321,6 +321,7 @@ struct pci_dev {
unsigned int multifunction:1;/* Part of multi-function device */
/* keep track of device state */
unsigned int is_added:1;
+ unsigned int is_gone:1;
unsigned int is_busmaster:1; /* device is busmaster */
unsigned int no_msi:1; /* device may not use msi */
unsigned int block_cfg_access:1; /* config space access is blocked */
@@ -1021,6 +1022,8 @@ void set_pcie_hotplug_bridge(struct pci_
int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap);
unsigned int pci_rescan_bus_bridge_resize(struct pci_dev *bridge);
unsigned int pci_rescan_bus(struct pci_bus *bus);
+void lock_pci_remove_rescan(void);
+void unlock_pci_remove_rescan(void);
/* Vital product data routines */
ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf);
===================================================================
@@ -298,6 +298,17 @@ msi_bus_store(struct device *dev, struct
static DEVICE_ATTR_RW(msi_bus);
static DEFINE_MUTEX(pci_remove_rescan_mutex);
+
+void lock_pci_remove_rescan(void)
+{
+ mutex_lock(&pci_remove_rescan_mutex);
+}
+
+void unlock_pci_remove_rescan(void)
+{
+ mutex_unlock(&pci_remove_rescan_mutex);
+}
+
static ssize_t bus_rescan_store(struct bus_type *bus, const char *buf,
size_t count)
{
===================================================================
@@ -34,6 +34,7 @@ static void pci_stop_dev(struct pci_dev
static void pci_destroy_dev(struct pci_dev *dev)
{
+ dev->is_gone = 1;
device_del(&dev->dev);
down_write(&pci_bus_sem);
@@ -109,8 +110,10 @@ static void pci_remove_bus_device(struct
*/
void pci_stop_and_remove_bus_device(struct pci_dev *dev)
{
- pci_stop_bus_device(dev);
- pci_remove_bus_device(dev);
+ if (!dev->is_gone) {
+ pci_stop_bus_device(dev);
+ pci_remove_bus_device(dev);
+ }
}
EXPORT_SYMBOL(pci_stop_and_remove_bus_device);
===================================================================
@@ -71,6 +71,7 @@ struct acpiphp_bridge {
struct acpiphp_context *context;
int nr_slots;
+ bool is_going_away;
/* This bus (host bridge) or Secondary bus (PCI-to-PCI bridge) */
struct pci_bus *pci_bus;
===================================================================
@@ -439,6 +439,13 @@ static void cleanup_bridge(struct acpiph
mutex_lock(&bridge_mutex);
list_del(&bridge->list);
mutex_unlock(&bridge_mutex);
+
+ /*
+ * For non-root bridges this flag is protected by the PCI remove/rescan
+ * locking. For root bridges it is only operated under acpi_scan_lock
+ * anyway.
+ */
+ bridge->is_going_away = true;
}
/**
@@ -733,11 +740,17 @@ static void trim_stale_devices(struct pc
*
* Iterate over all slots under this bridge and make sure that if a
* card is present they are enabled, and if not they are disabled.
+ *
+ * For non-root bridges call under the PCI remove/rescan mutex.
*/
static void acpiphp_check_bridge(struct acpiphp_bridge *bridge)
{
struct acpiphp_slot *slot;
+ /* Bail out if the bridge is going away. */
+ if (bridge->is_going_away)
+ return;
+
list_for_each_entry(slot, &bridge->slots, node) {
struct pci_bus *bus = slot->bus;
struct pci_dev *dev, *tmp;
@@ -878,14 +891,19 @@ static void hotplug_event_work(void *dat
{
struct acpiphp_context *context = data;
acpi_handle handle = context->handle;
+ struct acpiphp_bridge *bridge = context->func.parent;
acpi_scan_lock_acquire();
+ lock_pci_remove_rescan();
- hotplug_event(handle, type, context);
+ /* Bail out if the parent bridge is going away. */
+ if (!bridge->is_going_away)
+ hotplug_event(handle, type, context);
+ unlock_pci_remove_rescan();
acpi_scan_lock_release();
acpi_evaluate_hotplug_ost(handle, type, ACPI_OST_SC_SUCCESS, NULL);
- put_bridge(context->func.parent);
+ put_bridge(bridge);
}
/**
===================================================================
@@ -616,6 +616,8 @@ static void acpi_pci_root_remove(struct
{
struct acpi_pci_root *root = acpi_driver_data(device);
+ lock_pci_remove_rescan();
+
pci_stop_root_bus(root->bus);
device_set_run_wake(root->bus->bridge, false);
@@ -623,6 +625,8 @@ static void acpi_pci_root_remove(struct
pci_remove_root_bus(root->bus);
+ unlock_pci_remove_rescan();
+
kfree(root);
}