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>
This commit is contained in:
Rafael J. Wysocki 2013-11-22 21:55:07 +01:00
parent 1ceaba05b4
commit c27b2c33b6
3 changed files with 68 additions and 79 deletions

View File

@ -89,7 +89,7 @@ void acpi_device_add_finalize(struct acpi_device *device);
void acpi_free_pnp_ids(struct acpi_device_pnp *pnp); void acpi_free_pnp_ids(struct acpi_device_pnp *pnp);
int acpi_bind_one(struct device *dev, acpi_handle handle); int acpi_bind_one(struct device *dev, acpi_handle handle);
int acpi_unbind_one(struct device *dev); int acpi_unbind_one(struct device *dev);
void acpi_bus_device_eject(void *data, u32 ost_src); void acpi_device_hotplug(void *data, u32 ost_src);
bool acpi_device_is_present(struct acpi_device *adev); bool acpi_device_is_present(struct acpi_device *adev);
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------

View File

@ -683,11 +683,13 @@ static void hotplug_event_root(void *data, u32 type)
if (!root) if (!root)
break; break;
acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST,
ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
get_device(&root->device->dev); get_device(&root->device->dev);
acpi_scan_lock_release(); acpi_scan_lock_release();
acpi_bus_device_eject(root->device, ACPI_NOTIFY_EJECT_REQUEST); acpi_device_hotplug(root->device, ACPI_NOTIFY_EJECT_REQUEST);
return; return;
default: default:
acpi_handle_warn(handle, acpi_handle_warn(handle,

View File

@ -206,12 +206,8 @@ static int acpi_scan_hot_remove(struct acpi_device *device)
acpi_status status; acpi_status status;
unsigned long long sta; unsigned long long sta;
/* If there is no handle, the device node has been unregistered. */ if (device->handler && device->handler->hotplug.mode == AHM_CONTAINER)
if (!handle) { kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE);
dev_dbg(&device->dev, "ACPI handle missing\n");
put_device(&device->dev);
return -EINVAL;
}
/* /*
* Carry out two passes here and ignore errors in the first pass, * 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"); dev_warn(errdev, "Offline disabled.\n");
acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
acpi_bus_online, NULL, NULL, NULL); acpi_bus_online, NULL, NULL, NULL);
put_device(&device->dev);
return -EPERM; return -EPERM;
} }
acpi_bus_offline(handle, 0, (void *)false, (void **)&errdev); 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_walk_namespace(ACPI_TYPE_ANY, handle,
ACPI_UINT32_MAX, acpi_bus_online, ACPI_UINT32_MAX, acpi_bus_online,
NULL, NULL, NULL); NULL, NULL, NULL);
put_device(&device->dev);
return -EBUSY; return -EBUSY;
} }
} }
@ -259,9 +253,6 @@ static int acpi_scan_hot_remove(struct acpi_device *device)
acpi_bus_trim(device); acpi_bus_trim(device);
put_device(&device->dev);
device = NULL;
acpi_evaluate_lck(handle, 0); acpi_evaluate_lck(handle, 0);
/* /*
* TBD: _EJD support. * TBD: _EJD support.
@ -288,77 +279,74 @@ static int acpi_scan_hot_remove(struct acpi_device *device)
return 0; 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; int error;
lock_device_hotplug(); /*
mutex_lock(&acpi_scan_lock); * This function is only called for device objects for which matching
* scan handlers exist. The only situation in which the scan handler is
if (ost_src == ACPI_NOTIFY_EJECT_REQUEST) * not attached to this device object yet is when the device has just
acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST, * appeared (either it wasn't present at all before or it was removed
ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); * and then added again).
*/
if (device->handler && device->handler->hotplug.mode == AHM_CONTAINER) if (adev->handler) {
kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE); dev_warn(&adev->dev, "Already enumerated\n");
return -EBUSY;
error = acpi_scan_hot_remove(device);
if (error == -EPERM) {
goto err_support;
} else if (error) {
goto err_out;
} }
error = acpi_bus_scan(adev->handle);
out: if (error) {
mutex_unlock(&acpi_scan_lock); dev_warn(&adev->dev, "Namespace scan failure\n");
unlock_device_hotplug(); return error;
return; }
if (adev->handler) {
err_support: if (adev->handler->hotplug.mode == AHM_CONTAINER)
ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED; kobject_uevent(&adev->dev.kobj, KOBJ_ONLINE);
err_out: } else {
acpi_evaluate_hotplug_ost(handle, ost_src, ost_code, NULL); dev_warn(&adev->dev, "Enumeration failure\n");
goto out; 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; u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
struct acpi_device *adev = data;
int error; int error;
lock_device_hotplug(); lock_device_hotplug();
mutex_lock(&acpi_scan_lock); mutex_lock(&acpi_scan_lock);
if (ost_source != ACPI_NOTIFY_BUS_CHECK) { /*
device = NULL; * The device object's ACPI handle cannot become invalid as long as we
acpi_bus_get_device(handle, &device); * are holding acpi_scan_lock, but it may have become invalid before
if (acpi_device_enumerated(device)) { * that lock was acquired.
dev_warn(&device->dev, "Attempt to re-insert\n"); */
goto out; if (adev->handle == INVALID_ACPI_HANDLE)
}
}
error = acpi_bus_scan(handle);
if (error) {
acpi_handle_warn(handle, "Namespace scan failure\n");
goto out; 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;
} }
device = NULL; if (!error)
acpi_bus_get_device(handle, &device); ost_code = ACPI_OST_SC_SUCCESS;
if (!acpi_device_enumerated(device)) {
acpi_handle_warn(handle, "Device not enumerated\n");
goto out;
}
ost_code = ACPI_OST_SC_SUCCESS;
if (device->handler && device->handler->hotplug.mode == AHM_CONTAINER)
kobject_uevent(&device->dev.kobj, KOBJ_ONLINE);
out: 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); mutex_unlock(&acpi_scan_lock);
unlock_device_hotplug(); unlock_device_hotplug();
} }
@ -370,6 +358,9 @@ static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
struct acpi_device *adev; struct acpi_device *adev;
acpi_status status; acpi_status status;
if (acpi_bus_get_device(handle, &adev))
goto err_out;
switch (type) { switch (type) {
case ACPI_NOTIFY_BUS_CHECK: case ACPI_NOTIFY_BUS_CHECK:
acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n"); 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; ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
goto err_out; goto err_out;
} }
if (acpi_bus_get_device(handle, &adev)) acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST,
goto err_out; ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
break;
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;
default: default:
/* non-hotplug event; possibly handled by other handler */ /* non-hotplug event; possibly handled by other handler */
return; 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)) if (ACPI_SUCCESS(status))
return; return;
put_device(&adev->dev);
err_out: err_out:
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL); 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_evaluate_hotplug_ost(acpi_device->handle, ACPI_OST_EC_OSPM_EJECT,
ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
get_device(&acpi_device->dev); 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); ACPI_OST_EC_OSPM_EJECT);
if (ACPI_SUCCESS(status)) if (ACPI_SUCCESS(status))
return count; return count;