diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index 1592dbe4f904..b6162be4df40 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -77,6 +77,8 @@ struct acpiphp_bridge { /* PCI-to-PCI bridge device */ struct pci_dev *pci_dev; + + bool is_going_away; }; @@ -150,6 +152,7 @@ struct acpiphp_attention_info /* slot flags */ #define SLOT_ENABLED (0x00000001) +#define SLOT_IS_GOING_AWAY (0x00000002) /* function flags */ @@ -169,7 +172,7 @@ void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *slot); typedef int (*acpiphp_callback)(struct acpiphp_slot *slot, void *data); int acpiphp_enable_slot(struct acpiphp_slot *slot); -int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot); +int acpiphp_disable_slot(struct acpiphp_slot *slot); u8 acpiphp_get_power_status(struct acpiphp_slot *slot); u8 acpiphp_get_attention_status(struct acpiphp_slot *slot); u8 acpiphp_get_latch_status(struct acpiphp_slot *slot); diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c index dca66bc44578..728c31f4c2c5 100644 --- a/drivers/pci/hotplug/acpiphp_core.c +++ b/drivers/pci/hotplug/acpiphp_core.c @@ -156,7 +156,7 @@ static int disable_slot(struct hotplug_slot *hotplug_slot) pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot)); /* disable the specified slot */ - return acpiphp_disable_and_eject_slot(slot->acpi_slot); + return acpiphp_disable_slot(slot->acpi_slot); } diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 1cf605f67673..641ba6761bd7 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -430,6 +430,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); } @@ -437,6 +438,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; } /** @@ -736,6 +739,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; @@ -805,6 +812,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; @@ -834,6 +843,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); @@ -849,6 +861,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. @@ -878,9 +893,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); @@ -1048,12 +1065,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; } @@ -1061,10 +1085,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); @@ -1082,9 +1108,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 @@ -1095,7 +1130,6 @@ u8 acpiphp_get_power_status(struct acpiphp_slot *slot) return (slot->flags & SLOT_ENABLED); } - /* * latch open: 1 * latch closed: 0 @@ -1105,7 +1139,6 @@ u8 acpiphp_get_latch_status(struct acpiphp_slot *slot) return !(get_slot_status(slot) & ACPI_STA_DEVICE_UI); } - /* * adapter presence : 1 * absence : 0