diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-02-04 00:43:05 +0100 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-02-05 17:41:18 +0100 |
commit | 78ea4639a7647f2fcc957c3a532bde49df9895c7 (patch) | |
tree | 77a5f9ccf15bbba2c82a1613458fd68553c29888 /drivers/acpi/scan.c | |
parent | 7c2e17714e190b2ef857e7e842464fb47ceca146 (diff) |
ACPI / hotplug: Fix potential race in acpi_bus_notify()
There is a slight possibility for the ACPI device object pointed to
by adev in acpi_hotplug_notify_cb() to become invalid between the
acpi_bus_get_device() that it comes from and the subsequent dereference
of that pointer under get_device(). Namely, if acpi_scan_drop_device()
runs in parallel with acpi_hotplug_notify_cb(), acpi_device_del_work_fn()
queued up by it may delete the device object in question right after
a successful execution of acpi_bus_get_device() in acpi_bus_notify().
An analogous problem is present in acpi_bus_notify() where the device
pointer coming from acpi_bus_get_device() may become invalid before
it subsequent dereference in the "if" block.
To prevent that from happening, introduce a new function,
acpi_bus_get_acpi_device(), working analogously to acpi_bus_get_device()
except that it will grab a reference to the ACPI device object returned
by it and it will do that under the ACPICA's namespace mutex. Then,
make both acpi_hotplug_notify_cb() and acpi_bus_notify() use
acpi_bus_get_acpi_device() instead of acpi_bus_get_device() so as to
ensure that the pointers used by them will not become stale at one
point.
In addition to that, introduce acpi_bus_put_acpi_device() as a wrapper
around put_device() to be used along with acpi_bus_get_acpi_device()
and make the (new) users of the latter use acpi_bus_put_acpi_device()
too.
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 | 42 |
1 files changed, 34 insertions, 8 deletions
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 7384158c7f8..59eba29a606 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -476,7 +476,7 @@ static void acpi_device_hotplug(void *data, u32 src) out: acpi_evaluate_hotplug_ost(adev->handle, src, ost_code, NULL); - put_device(&adev->dev); + acpi_bus_put_acpi_device(adev); mutex_unlock(&acpi_scan_lock); unlock_device_hotplug(); } @@ -488,9 +488,6 @@ 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"); @@ -512,12 +509,15 @@ static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data) /* non-hotplug event; possibly handled by other handler */ return; } - get_device(&adev->dev); + adev = acpi_bus_get_acpi_device(handle); + if (!adev) + goto err_out; + status = acpi_hotplug_execute(acpi_device_hotplug, adev, type); if (ACPI_SUCCESS(status)) return; - put_device(&adev->dev); + acpi_bus_put_acpi_device(adev); err_out: acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL); @@ -1112,14 +1112,16 @@ static void acpi_scan_drop_device(acpi_handle handle, void *context) mutex_unlock(&acpi_device_del_lock); } -int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device) +static int acpi_get_device_data(acpi_handle handle, struct acpi_device **device, + void (*callback)(void *)) { acpi_status status; if (!device) return -EINVAL; - status = acpi_get_data(handle, acpi_scan_drop_device, (void **)device); + status = acpi_get_data_full(handle, acpi_scan_drop_device, + (void **)device, callback); if (ACPI_FAILURE(status) || !*device) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n", handle)); @@ -1127,8 +1129,32 @@ int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device) } return 0; } + +int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device) +{ + return acpi_get_device_data(handle, device, NULL); +} EXPORT_SYMBOL(acpi_bus_get_device); +static void get_acpi_device(void *dev) +{ + if (dev) + get_device(&((struct acpi_device *)dev)->dev); +} + +struct acpi_device *acpi_bus_get_acpi_device(acpi_handle handle) +{ + struct acpi_device *adev = NULL; + + acpi_get_device_data(handle, &adev, get_acpi_device); + return adev; +} + +void acpi_bus_put_acpi_device(struct acpi_device *adev) +{ + put_device(&adev->dev); +} + int acpi_device_add(struct acpi_device *device, void (*release)(struct device *)) { |