diff options
Diffstat (limited to 'drivers/pci/hotplug/acpiphp_glue.c')
-rw-r--r-- | drivers/pci/hotplug/acpiphp_glue.c | 45 |
1 files changed, 39 insertions, 6 deletions
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index e86439283a5d..cd929aed3613 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -432,6 +432,7 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) pr_err("failed to remove notify handler\n"); } } + slot->flags |= SLOT_IS_GOING_AWAY; if (slot->slot) acpiphp_unregister_hotplug_slot(slot); } @@ -439,6 +440,8 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) mutex_lock(&bridge_mutex); list_del(&bridge->list); mutex_unlock(&bridge_mutex); + + bridge->is_going_away = true; } /** @@ -491,7 +494,7 @@ static void acpiphp_bus_add(acpi_handle handle) acpi_bus_scan(handle); acpi_bus_get_device(handle, &adev); - if (adev) + if (acpi_device_enumerated(adev)) acpi_device_set_power(adev, ACPI_STATE_D0); } @@ -757,6 +760,10 @@ 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; @@ -827,6 +834,8 @@ void acpiphp_check_host_bridge(acpi_handle handle) } } +static int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot); + static void hotplug_event(acpi_handle handle, u32 type, void *data) { struct acpiphp_context *context = data; @@ -856,6 +865,9 @@ static void hotplug_event(acpi_handle handle, u32 type, void *data) } else { struct acpiphp_slot *slot = func->slot; + if (slot->flags & SLOT_IS_GOING_AWAY) + break; + mutex_lock(&slot->crit_sect); enable_slot(slot); mutex_unlock(&slot->crit_sect); @@ -871,6 +883,9 @@ static void hotplug_event(acpi_handle handle, u32 type, void *data) struct acpiphp_slot *slot = func->slot; int ret; + if (slot->flags & SLOT_IS_GOING_AWAY) + break; + /* * Check if anything has changed in the slot and rescan * from the parent if that's the case. @@ -900,9 +915,11 @@ static void hotplug_event_work(void *data, u32 type) acpi_handle handle = context->handle; acpi_scan_lock_acquire(); + pci_lock_rescan_remove(); hotplug_event(handle, type, context); + pci_unlock_rescan_remove(); acpi_scan_lock_release(); acpi_evaluate_hotplug_ost(handle, type, ACPI_OST_SC_SUCCESS, NULL); put_bridge(context->func.parent); @@ -1070,12 +1087,19 @@ void acpiphp_remove_slots(struct pci_bus *bus) */ int acpiphp_enable_slot(struct acpiphp_slot *slot) { + pci_lock_rescan_remove(); + + if (slot->flags & SLOT_IS_GOING_AWAY) + return -ENODEV; + mutex_lock(&slot->crit_sect); /* configure all functions */ if (!(slot->flags & SLOT_ENABLED)) enable_slot(slot); mutex_unlock(&slot->crit_sect); + + pci_unlock_rescan_remove(); return 0; } @@ -1083,10 +1107,12 @@ int acpiphp_enable_slot(struct acpiphp_slot *slot) * acpiphp_disable_and_eject_slot - power off and eject slot * @slot: ACPI PHP slot */ -int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot) +static int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot) { struct acpiphp_func *func; - int retval = 0; + + if (slot->flags & SLOT_IS_GOING_AWAY) + return -ENODEV; mutex_lock(&slot->crit_sect); @@ -1104,9 +1130,18 @@ int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot) } mutex_unlock(&slot->crit_sect); - return retval; + return 0; } +int acpiphp_disable_slot(struct acpiphp_slot *slot) +{ + int ret; + + pci_lock_rescan_remove(); + ret = acpiphp_disable_and_eject_slot(slot); + pci_unlock_rescan_remove(); + return ret; +} /* * slot enabled: 1 @@ -1117,7 +1152,6 @@ u8 acpiphp_get_power_status(struct acpiphp_slot *slot) return (slot->flags & SLOT_ENABLED); } - /* * latch open: 1 * latch closed: 0 @@ -1127,7 +1161,6 @@ u8 acpiphp_get_latch_status(struct acpiphp_slot *slot) return !(get_slot_status(slot) & ACPI_STA_DEVICE_UI); } - /* * adapter presence : 1 * absence : 0 |