aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2010-10-21 20:35:54 -0400
committerLen Brown <len.brown@intel.com>2010-10-23 01:56:14 -0400
commit3e384ee6c687cb397581ee8f9440fc8220cfac80 (patch)
treeb8559d4952bb6d7350eeb5a0bac418a5eacda117
parentf6f94e2ab1b33f0082ac22d71f66385a60d8157f (diff)
ACPI / PM: Fix reference counting of power resources
The reference counting of ACPI power resources is currently broken for a few reasons. First, instead of using a simple reference counter per power resource it uses a list of objects representing refereces to the given power resource from devices. This leads to the second breakage, because it prevents power resources from being referenced more than once by one device, which is necessary if the device is configured to signal wakeup. Namely, when putting the device into a low power state we first call acpi_enable_wakeup_device_power() that should reference count power resources needed for signaling wakeup and then we call acpi_power_transition() to power off the device. The latter call drops references to the device's power resources, possibly including the ones added by acpi_enable_wakeup_device_power(), so the device can't signal wakeup as a result. Apart from this, the locking in acpi_power_on() and acpi_power_off_device() doesn't prevent all possible races from happening, which may be problematic for runtime PM and asynchronous suspend and resume. Fix the problem by using a counter for power resources reference counting and putting the evaluation of ACPI _ON and _OFF methods under the power resource mutex. Reported-by: Matthew Garrett <mjg@redhat.com> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Signed-off-by: Len Brown <len.brown@intel.com>
-rw-r--r--drivers/acpi/power.c167
1 files changed, 70 insertions, 97 deletions
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c
index 844c155aeb0f..67dedeed144c 100644
--- a/drivers/acpi/power.c
+++ b/drivers/acpi/power.c
@@ -80,18 +80,13 @@ static struct acpi_driver acpi_power_driver = {
80 }, 80 },
81}; 81};
82 82
83struct acpi_power_reference {
84 struct list_head node;
85 struct acpi_device *device;
86};
87
88struct acpi_power_resource { 83struct acpi_power_resource {
89 struct acpi_device * device; 84 struct acpi_device * device;
90 acpi_bus_id name; 85 acpi_bus_id name;
91 u32 system_level; 86 u32 system_level;
92 u32 order; 87 u32 order;
88 unsigned int ref_count;
93 struct mutex resource_lock; 89 struct mutex resource_lock;
94 struct list_head reference;
95}; 90};
96 91
97static struct list_head acpi_power_resource_list; 92static struct list_head acpi_power_resource_list;
@@ -184,101 +179,89 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state)
184 return result; 179 return result;
185} 180}
186 181
187static int acpi_power_on(acpi_handle handle, struct acpi_device *dev) 182static int __acpi_power_on(struct acpi_power_resource *resource)
188{ 183{
189 int result = 0;
190 int found = 0;
191 acpi_status status = AE_OK; 184 acpi_status status = AE_OK;
192 struct acpi_power_resource *resource = NULL;
193 struct list_head *node, *next;
194 struct acpi_power_reference *ref;
195 185
186 status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL);
187 if (ACPI_FAILURE(status))
188 return -ENODEV;
189
190 /* Update the power resource's _device_ power state */
191 resource->device->power.state = ACPI_STATE_D0;
192
193 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Power resource [%s] turned on\n",
194 resource->name));
195
196 return 0;
197}
198
199static int acpi_power_on(acpi_handle handle)
200{
201 int result = 0;
202 struct acpi_power_resource *resource = NULL;
196 203
197 result = acpi_power_get_context(handle, &resource); 204 result = acpi_power_get_context(handle, &resource);
198 if (result) 205 if (result)
199 return result; 206 return result;
200 207
201 mutex_lock(&resource->resource_lock); 208 mutex_lock(&resource->resource_lock);
202 list_for_each_safe(node, next, &resource->reference) {
203 ref = container_of(node, struct acpi_power_reference, node);
204 if (dev->handle == ref->device->handle) {
205 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] already referenced by resource [%s]\n",
206 dev->pnp.bus_id, resource->name));
207 found = 1;
208 break;
209 }
210 }
211 209
212 if (!found) { 210 if (resource->ref_count++) {
213 ref = kmalloc(sizeof (struct acpi_power_reference), 211 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
214 irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL); 212 "Power resource [%s] already on",
215 if (!ref) { 213 resource->name));
216 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "kmalloc() failed\n")); 214 } else {
217 mutex_unlock(&resource->resource_lock); 215 result = __acpi_power_on(resource);
218 return -ENOMEM;
219 }
220 list_add_tail(&ref->node, &resource->reference);
221 ref->device = dev;
222 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] added to resource [%s] references\n",
223 dev->pnp.bus_id, resource->name));
224 } 216 }
225 mutex_unlock(&resource->resource_lock);
226 217
227 status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL); 218 mutex_unlock(&resource->resource_lock);
228 if (ACPI_FAILURE(status))
229 return -ENODEV;
230
231 /* Update the power resource's _device_ power state */
232 resource->device->power.state = ACPI_STATE_D0;
233 219
234 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned on\n",
235 resource->name));
236 return 0; 220 return 0;
237} 221}
238 222
239static int acpi_power_off_device(acpi_handle handle, struct acpi_device *dev) 223static int acpi_power_off_device(acpi_handle handle)
240{ 224{
241 int result = 0; 225 int result = 0;
242 acpi_status status = AE_OK; 226 acpi_status status = AE_OK;
243 struct acpi_power_resource *resource = NULL; 227 struct acpi_power_resource *resource = NULL;
244 struct list_head *node, *next;
245 struct acpi_power_reference *ref;
246 228
247 result = acpi_power_get_context(handle, &resource); 229 result = acpi_power_get_context(handle, &resource);
248 if (result) 230 if (result)
249 return result; 231 return result;
250 232
251 mutex_lock(&resource->resource_lock); 233 mutex_lock(&resource->resource_lock);
252 list_for_each_safe(node, next, &resource->reference) { 234
253 ref = container_of(node, struct acpi_power_reference, node); 235 if (!resource->ref_count) {
254 if (dev->handle == ref->device->handle) { 236 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
255 list_del(&ref->node); 237 "Power resource [%s] already off",
256 kfree(ref); 238 resource->name));
257 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] removed from resource [%s] references\n", 239 goto unlock;
258 dev->pnp.bus_id, resource->name));
259 break;
260 }
261 } 240 }
262 241
263 if (!list_empty(&resource->reference)) { 242 if (--resource->ref_count) {
264 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cannot turn resource [%s] off - resource is in use\n", 243 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
265 resource->name)); 244 "Power resource [%s] still in use\n",
266 mutex_unlock(&resource->resource_lock); 245 resource->name));
267 return 0; 246 goto unlock;
268 } 247 }
269 mutex_unlock(&resource->resource_lock);
270 248
271 status = acpi_evaluate_object(resource->device->handle, "_OFF", NULL, NULL); 249 status = acpi_evaluate_object(resource->device->handle, "_OFF", NULL, NULL);
272 if (ACPI_FAILURE(status)) 250 if (ACPI_FAILURE(status)) {
273 return -ENODEV; 251 result = -ENODEV;
252 } else {
253 /* Update the power resource's _device_ power state */
254 resource->device->power.state = ACPI_STATE_D3;
274 255
275 /* Update the power resource's _device_ power state */ 256 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
276 resource->device->power.state = ACPI_STATE_D3; 257 "Power resource [%s] turned off\n",
258 resource->name));
259 }
277 260
278 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned off\n", 261 unlock:
279 resource->name)); 262 mutex_unlock(&resource->resource_lock);
280 263
281 return 0; 264 return result;
282} 265}
283 266
284/** 267/**
@@ -364,7 +347,7 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state)
364 347
365 /* Open power resource */ 348 /* Open power resource */
366 for (i = 0; i < dev->wakeup.resources.count; i++) { 349 for (i = 0; i < dev->wakeup.resources.count; i++) {
367 int ret = acpi_power_on(dev->wakeup.resources.handles[i], dev); 350 int ret = acpi_power_on(dev->wakeup.resources.handles[i]);
368 if (ret) { 351 if (ret) {
369 printk(KERN_ERR PREFIX "Transition power state\n"); 352 printk(KERN_ERR PREFIX "Transition power state\n");
370 dev->wakeup.flags.valid = 0; 353 dev->wakeup.flags.valid = 0;
@@ -420,7 +403,7 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev)
420 /* Close power resource */ 403 /* Close power resource */
421 for (i = 0; i < dev->wakeup.resources.count; i++) { 404 for (i = 0; i < dev->wakeup.resources.count; i++) {
422 int ret = acpi_power_off_device( 405 int ret = acpi_power_off_device(
423 dev->wakeup.resources.handles[i], dev); 406 dev->wakeup.resources.handles[i]);
424 if (ret) { 407 if (ret) {
425 printk(KERN_ERR PREFIX "Transition power state\n"); 408 printk(KERN_ERR PREFIX "Transition power state\n");
426 dev->wakeup.flags.valid = 0; 409 dev->wakeup.flags.valid = 0;
@@ -500,7 +483,7 @@ int acpi_power_transition(struct acpi_device *device, int state)
500 * (e.g. so the device doesn't lose power while transitioning). 483 * (e.g. so the device doesn't lose power while transitioning).
501 */ 484 */
502 for (i = 0; i < tl->count; i++) { 485 for (i = 0; i < tl->count; i++) {
503 result = acpi_power_on(tl->handles[i], device); 486 result = acpi_power_on(tl->handles[i]);
504 if (result) 487 if (result)
505 goto end; 488 goto end;
506 } 489 }
@@ -513,7 +496,7 @@ int acpi_power_transition(struct acpi_device *device, int state)
513 * Then we dereference all power resources used in the current list. 496 * Then we dereference all power resources used in the current list.
514 */ 497 */
515 for (i = 0; i < cl->count; i++) { 498 for (i = 0; i < cl->count; i++) {
516 result = acpi_power_off_device(cl->handles[i], device); 499 result = acpi_power_off_device(cl->handles[i]);
517 if (result) 500 if (result)
518 goto end; 501 goto end;
519 } 502 }
@@ -551,7 +534,6 @@ static int acpi_power_add(struct acpi_device *device)
551 534
552 resource->device = device; 535 resource->device = device;
553 mutex_init(&resource->resource_lock); 536 mutex_init(&resource->resource_lock);
554 INIT_LIST_HEAD(&resource->reference);
555 strcpy(resource->name, device->pnp.bus_id); 537 strcpy(resource->name, device->pnp.bus_id);
556 strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME); 538 strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME);
557 strcpy(acpi_device_class(device), ACPI_POWER_CLASS); 539 strcpy(acpi_device_class(device), ACPI_POWER_CLASS);
@@ -594,22 +576,14 @@ static int acpi_power_add(struct acpi_device *device)
594 576
595static int acpi_power_remove(struct acpi_device *device, int type) 577static int acpi_power_remove(struct acpi_device *device, int type)
596{ 578{
597 struct acpi_power_resource *resource = NULL; 579 struct acpi_power_resource *resource;
598 struct list_head *node, *next;
599 580
600 581 if (!device)
601 if (!device || !acpi_driver_data(device))
602 return -EINVAL; 582 return -EINVAL;
603 583
604 resource = acpi_driver_data(device); 584 resource = acpi_driver_data(device);
605 585 if (!resource)
606 mutex_lock(&resource->resource_lock); 586 return -EINVAL;
607 list_for_each_safe(node, next, &resource->reference) {
608 struct acpi_power_reference *ref = container_of(node, struct acpi_power_reference, node);
609 list_del(&ref->node);
610 kfree(ref);
611 }
612 mutex_unlock(&resource->resource_lock);
613 587
614 kfree(resource); 588 kfree(resource);
615 589
@@ -619,29 +593,28 @@ static int acpi_power_remove(struct acpi_device *device, int type)
619static int acpi_power_resume(struct acpi_device *device) 593static int acpi_power_resume(struct acpi_device *device)
620{ 594{
621 int result = 0, state; 595 int result = 0, state;
622 struct acpi_power_resource *resource = NULL; 596 struct acpi_power_resource *resource;
623 struct acpi_power_reference *ref;
624 597
625 if (!device || !acpi_driver_data(device)) 598 if (!device)
626 return -EINVAL; 599 return -EINVAL;
627 600
628 resource = acpi_driver_data(device); 601 resource = acpi_driver_data(device);
602 if (!resource)
603 return -EINVAL;
604
605 mutex_lock(&resource->resource_lock);
629 606
630 result = acpi_power_get_state(device->handle, &state); 607 result = acpi_power_get_state(device->handle, &state);
631 if (result) 608 if (result)
632 return result; 609 goto unlock;
633 610
634 mutex_lock(&resource->resource_lock); 611 if (state == ACPI_POWER_RESOURCE_STATE_OFF && resource->ref_count)
635 if (state == ACPI_POWER_RESOURCE_STATE_OFF && 612 result = __acpi_power_on(resource);
636 !list_empty(&resource->reference)) {
637 ref = container_of(resource->reference.next, struct acpi_power_reference, node);
638 mutex_unlock(&resource->resource_lock);
639 result = acpi_power_on(device->handle, ref->device);
640 return result;
641 }
642 613
614 unlock:
643 mutex_unlock(&resource->resource_lock); 615 mutex_unlock(&resource->resource_lock);
644 return 0; 616
617 return result;
645} 618}
646 619
647int __init acpi_power_init(void) 620int __init acpi_power_init(void)