diff options
author | Lin Ming <ming.m.lin@intel.com> | 2012-09-13 18:26:33 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2012-09-13 18:26:33 -0400 |
commit | 40bf66ec9791f1452b90b82aadc3b6e6aee201f5 (patch) | |
tree | 677ca8a83277992ed9b8ad2ac75239fccc7f9e2d /drivers/acpi/power.c | |
parent | 8f7412a792bc989d1bddd3c802282eec09456d57 (diff) |
ACPI / PM: Fix resource_lock dead lock in acpi_power_on_device
Commit 0090def("ACPI: Add interface to register/unregister device
to/from power resources") used resource_lock to protect the devices list
that relies on power resource. It caused a mutex dead lock, as below
acpi_power_on ---> lock resource_lock
__acpi_power_on
acpi_power_on_device
acpi_power_get_inferred_state
acpi_power_get_list_state ---> lock resource_lock
This patch adds a new mutex "devices_lock" to protect the devices list
and calls acpi_power_on_device in acpi_power_on, instead of
__acpi_power_on, after the resource_lock is released.
[rjw: Changed data type of a boolean variable to bool.]
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Aaron Lu <aaron.lu@intel.com>
Cc: stable@vger.kernel.org
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Diffstat (limited to 'drivers/acpi/power.c')
-rw-r--r-- | drivers/acpi/power.c | 34 |
1 files changed, 23 insertions, 11 deletions
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index fc1803414629..cc2a2dcff336 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c | |||
@@ -107,6 +107,7 @@ struct acpi_power_resource { | |||
107 | 107 | ||
108 | /* List of devices relying on this power resource */ | 108 | /* List of devices relying on this power resource */ |
109 | struct acpi_power_resource_device *devices; | 109 | struct acpi_power_resource_device *devices; |
110 | struct mutex devices_lock; | ||
110 | }; | 111 | }; |
111 | 112 | ||
112 | static struct list_head acpi_power_resource_list; | 113 | static struct list_head acpi_power_resource_list; |
@@ -225,7 +226,6 @@ static void acpi_power_on_device(struct acpi_power_managed_device *device) | |||
225 | 226 | ||
226 | static int __acpi_power_on(struct acpi_power_resource *resource) | 227 | static int __acpi_power_on(struct acpi_power_resource *resource) |
227 | { | 228 | { |
228 | struct acpi_power_resource_device *device_list = resource->devices; | ||
229 | acpi_status status = AE_OK; | 229 | acpi_status status = AE_OK; |
230 | 230 | ||
231 | status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL); | 231 | status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL); |
@@ -238,19 +238,15 @@ static int __acpi_power_on(struct acpi_power_resource *resource) | |||
238 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Power resource [%s] turned on\n", | 238 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Power resource [%s] turned on\n", |
239 | resource->name)); | 239 | resource->name)); |
240 | 240 | ||
241 | while (device_list) { | ||
242 | acpi_power_on_device(device_list->device); | ||
243 | |||
244 | device_list = device_list->next; | ||
245 | } | ||
246 | |||
247 | return 0; | 241 | return 0; |
248 | } | 242 | } |
249 | 243 | ||
250 | static int acpi_power_on(acpi_handle handle) | 244 | static int acpi_power_on(acpi_handle handle) |
251 | { | 245 | { |
252 | int result = 0; | 246 | int result = 0; |
247 | bool resume_device = false; | ||
253 | struct acpi_power_resource *resource = NULL; | 248 | struct acpi_power_resource *resource = NULL; |
249 | struct acpi_power_resource_device *device_list; | ||
254 | 250 | ||
255 | result = acpi_power_get_context(handle, &resource); | 251 | result = acpi_power_get_context(handle, &resource); |
256 | if (result) | 252 | if (result) |
@@ -266,10 +262,25 @@ static int acpi_power_on(acpi_handle handle) | |||
266 | result = __acpi_power_on(resource); | 262 | result = __acpi_power_on(resource); |
267 | if (result) | 263 | if (result) |
268 | resource->ref_count--; | 264 | resource->ref_count--; |
265 | else | ||
266 | resume_device = true; | ||
269 | } | 267 | } |
270 | 268 | ||
271 | mutex_unlock(&resource->resource_lock); | 269 | mutex_unlock(&resource->resource_lock); |
272 | 270 | ||
271 | if (!resume_device) | ||
272 | return result; | ||
273 | |||
274 | mutex_lock(&resource->devices_lock); | ||
275 | |||
276 | device_list = resource->devices; | ||
277 | while (device_list) { | ||
278 | acpi_power_on_device(device_list->device); | ||
279 | device_list = device_list->next; | ||
280 | } | ||
281 | |||
282 | mutex_unlock(&resource->devices_lock); | ||
283 | |||
273 | return result; | 284 | return result; |
274 | } | 285 | } |
275 | 286 | ||
@@ -355,7 +366,7 @@ static void __acpi_power_resource_unregister_device(struct device *dev, | |||
355 | if (acpi_power_get_context(res_handle, &resource)) | 366 | if (acpi_power_get_context(res_handle, &resource)) |
356 | return; | 367 | return; |
357 | 368 | ||
358 | mutex_lock(&resource->resource_lock); | 369 | mutex_lock(&resource->devices_lock); |
359 | prev = NULL; | 370 | prev = NULL; |
360 | curr = resource->devices; | 371 | curr = resource->devices; |
361 | while (curr) { | 372 | while (curr) { |
@@ -372,7 +383,7 @@ static void __acpi_power_resource_unregister_device(struct device *dev, | |||
372 | prev = curr; | 383 | prev = curr; |
373 | curr = curr->next; | 384 | curr = curr->next; |
374 | } | 385 | } |
375 | mutex_unlock(&resource->resource_lock); | 386 | mutex_unlock(&resource->devices_lock); |
376 | } | 387 | } |
377 | 388 | ||
378 | /* Unlink dev from all power resources in _PR0 */ | 389 | /* Unlink dev from all power resources in _PR0 */ |
@@ -414,10 +425,10 @@ static int __acpi_power_resource_register_device( | |||
414 | 425 | ||
415 | power_resource_device->device = powered_device; | 426 | power_resource_device->device = powered_device; |
416 | 427 | ||
417 | mutex_lock(&resource->resource_lock); | 428 | mutex_lock(&resource->devices_lock); |
418 | power_resource_device->next = resource->devices; | 429 | power_resource_device->next = resource->devices; |
419 | resource->devices = power_resource_device; | 430 | resource->devices = power_resource_device; |
420 | mutex_unlock(&resource->resource_lock); | 431 | mutex_unlock(&resource->devices_lock); |
421 | 432 | ||
422 | return 0; | 433 | return 0; |
423 | } | 434 | } |
@@ -721,6 +732,7 @@ static int acpi_power_add(struct acpi_device *device) | |||
721 | 732 | ||
722 | resource->device = device; | 733 | resource->device = device; |
723 | mutex_init(&resource->resource_lock); | 734 | mutex_init(&resource->resource_lock); |
735 | mutex_init(&resource->devices_lock); | ||
724 | strcpy(resource->name, device->pnp.bus_id); | 736 | strcpy(resource->name, device->pnp.bus_id); |
725 | strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME); | 737 | strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME); |
726 | strcpy(acpi_device_class(device), ACPI_POWER_CLASS); | 738 | strcpy(acpi_device_class(device), ACPI_POWER_CLASS); |