diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-11-23 00:55:07 +0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-11-23 00:55:07 +0400 |
commit | c27b2c33b6215eeb3d5c290ac889ab6d543f6207 (patch) | |
tree | 5eba733de6ca18f682e2704069c8a014e93fa26b /drivers/acpi/scan.c | |
parent | 1ceaba05b4afb4bd7b4b4801f2718c13f59321eb (diff) | |
download | linux-c27b2c33b6215eeb3d5c290ac889ab6d543f6207.tar.xz |
ACPI / hotplug: Introduce common hotplug function acpi_device_hotplug()
Modify the common ACPI device hotplug code to always queue up the
same function, acpi_device_hotplug(), using acpi_hotplug_execute()
and make the PCI host bridge hotplug code use that function too for
device hot removal.
This allows some code duplication to be reduced and a race condition
where the relevant ACPI handle may become invalid between the
notification handler and the function queued up by it via
acpi_hotplug_execute() to be avoided.
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Tested-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Diffstat (limited to 'drivers/acpi/scan.c')
-rw-r--r-- | drivers/acpi/scan.c | 141 |
1 files changed, 64 insertions, 77 deletions
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 4fa416f94f52..dd0ff9de9277 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -206,12 +206,8 @@ static int acpi_scan_hot_remove(struct acpi_device *device) acpi_status status; unsigned long long sta; - /* If there is no handle, the device node has been unregistered. */ - if (!handle) { - dev_dbg(&device->dev, "ACPI handle missing\n"); - put_device(&device->dev); - return -EINVAL; - } + if (device->handler && device->handler->hotplug.mode == AHM_CONTAINER) + kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE); /* * Carry out two passes here and ignore errors in the first pass, @@ -230,7 +226,6 @@ static int acpi_scan_hot_remove(struct acpi_device *device) dev_warn(errdev, "Offline disabled.\n"); acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, acpi_bus_online, NULL, NULL, NULL); - put_device(&device->dev); return -EPERM; } acpi_bus_offline(handle, 0, (void *)false, (void **)&errdev); @@ -249,7 +244,6 @@ static int acpi_scan_hot_remove(struct acpi_device *device) acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, acpi_bus_online, NULL, NULL, NULL); - put_device(&device->dev); return -EBUSY; } } @@ -259,9 +253,6 @@ static int acpi_scan_hot_remove(struct acpi_device *device) acpi_bus_trim(device); - put_device(&device->dev); - device = NULL; - acpi_evaluate_lck(handle, 0); /* * TBD: _EJD support. @@ -288,77 +279,74 @@ static int acpi_scan_hot_remove(struct acpi_device *device) return 0; } -void acpi_bus_device_eject(void *data, u32 ost_src) +static int acpi_scan_device_check(struct acpi_device *adev) { - struct acpi_device *device = data; - acpi_handle handle = device->handle; - u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; int error; - lock_device_hotplug(); - mutex_lock(&acpi_scan_lock); - - if (ost_src == ACPI_NOTIFY_EJECT_REQUEST) - acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST, - ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); - - if (device->handler && device->handler->hotplug.mode == AHM_CONTAINER) - kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE); - - error = acpi_scan_hot_remove(device); - if (error == -EPERM) { - goto err_support; - } else if (error) { - goto err_out; + /* + * This function is only called for device objects for which matching + * scan handlers exist. The only situation in which the scan handler is + * not attached to this device object yet is when the device has just + * appeared (either it wasn't present at all before or it was removed + * and then added again). + */ + if (adev->handler) { + dev_warn(&adev->dev, "Already enumerated\n"); + return -EBUSY; } - - out: - mutex_unlock(&acpi_scan_lock); - unlock_device_hotplug(); - return; - - err_support: - ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED; - err_out: - acpi_evaluate_hotplug_ost(handle, ost_src, ost_code, NULL); - goto out; + error = acpi_bus_scan(adev->handle); + if (error) { + dev_warn(&adev->dev, "Namespace scan failure\n"); + return error; + } + if (adev->handler) { + if (adev->handler->hotplug.mode == AHM_CONTAINER) + kobject_uevent(&adev->dev.kobj, KOBJ_ONLINE); + } else { + dev_warn(&adev->dev, "Enumeration failure\n"); + return -ENODEV; + } + return 0; } -static void acpi_scan_bus_device_check(void *data, u32 ost_source) +void acpi_device_hotplug(void *data, u32 src) { - acpi_handle handle = data; - struct acpi_device *device; u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; + struct acpi_device *adev = data; int error; lock_device_hotplug(); mutex_lock(&acpi_scan_lock); - if (ost_source != ACPI_NOTIFY_BUS_CHECK) { - device = NULL; - acpi_bus_get_device(handle, &device); - if (acpi_device_enumerated(device)) { - dev_warn(&device->dev, "Attempt to re-insert\n"); - goto out; - } - } - error = acpi_bus_scan(handle); - if (error) { - acpi_handle_warn(handle, "Namespace scan failure\n"); - goto out; - } - device = NULL; - acpi_bus_get_device(handle, &device); - if (!acpi_device_enumerated(device)) { - acpi_handle_warn(handle, "Device not enumerated\n"); + /* + * The device object's ACPI handle cannot become invalid as long as we + * are holding acpi_scan_lock, but it may have become invalid before + * that lock was acquired. + */ + if (adev->handle == INVALID_ACPI_HANDLE) goto out; + + switch (src) { + case ACPI_NOTIFY_BUS_CHECK: + error = acpi_bus_scan(adev->handle); + break; + case ACPI_NOTIFY_DEVICE_CHECK: + error = acpi_scan_device_check(adev); + break; + case ACPI_NOTIFY_EJECT_REQUEST: + case ACPI_OST_EC_OSPM_EJECT: + error = acpi_scan_hot_remove(adev); + break; + default: + error = -EINVAL; + break; } - ost_code = ACPI_OST_SC_SUCCESS; - if (device->handler && device->handler->hotplug.mode == AHM_CONTAINER) - kobject_uevent(&device->dev.kobj, KOBJ_ONLINE); + if (!error) + ost_code = ACPI_OST_SC_SUCCESS; out: - acpi_evaluate_hotplug_ost(handle, ost_source, ost_code, NULL); + acpi_evaluate_hotplug_ost(adev->handle, src, ost_code, NULL); + put_device(&adev->dev); mutex_unlock(&acpi_scan_lock); unlock_device_hotplug(); } @@ -370,6 +358,9 @@ static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data) struct acpi_device *adev; acpi_status status; + if (acpi_bus_get_device(handle, &adev)) + goto err_out; + switch (type) { case ACPI_NOTIFY_BUS_CHECK: acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n"); @@ -384,24 +375,20 @@ static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data) ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED; goto err_out; } - if (acpi_bus_get_device(handle, &adev)) - goto err_out; - - get_device(&adev->dev); - status = acpi_hotplug_execute(acpi_bus_device_eject, adev, type); - if (ACPI_SUCCESS(status)) - return; - - put_device(&adev->dev); - goto err_out; + acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST, + ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); + break; default: /* non-hotplug event; possibly handled by other handler */ return; } - status = acpi_hotplug_execute(acpi_scan_bus_device_check, handle, type); + get_device(&adev->dev); + status = acpi_hotplug_execute(acpi_device_hotplug, adev, type); if (ACPI_SUCCESS(status)) return; + put_device(&adev->dev); + err_out: acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL); } @@ -454,7 +441,7 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, acpi_evaluate_hotplug_ost(acpi_device->handle, ACPI_OST_EC_OSPM_EJECT, ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); get_device(&acpi_device->dev); - status = acpi_hotplug_execute(acpi_bus_device_eject, acpi_device, + status = acpi_hotplug_execute(acpi_device_hotplug, acpi_device, ACPI_OST_EC_OSPM_EJECT); if (ACPI_SUCCESS(status)) return count; |