diff options
author | Len Brown <len.brown@intel.com> | 2010-10-25 08:13:37 +0200 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2010-10-25 08:13:37 +0200 |
commit | 22156ea7bb631ac4d74b706f4875d41b2520dc26 (patch) | |
tree | 30bff9e9bbfc92a8f7623ce120c011a5c3ff93d9 /drivers | |
parent | Merge branch 'cleanup' into release (diff) | |
parent | ACPI / PM: Fix reference counting of power resources (diff) | |
download | linux-22156ea7bb631ac4d74b706f4875d41b2520dc26.tar.xz linux-22156ea7bb631ac4d74b706f4875d41b2520dc26.zip |
Merge branch 'power-refcount' into release
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/acpi/power.c | 167 |
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 = { }, }; -struct acpi_power_reference { - struct list_head node; - struct acpi_device *device; -}; - struct acpi_power_resource { struct acpi_device * device; acpi_bus_id name; u32 system_level; u32 order; + unsigned int ref_count; struct mutex resource_lock; - struct list_head reference; }; static 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) return result; } -static int acpi_power_on(acpi_handle handle, struct acpi_device *dev) +static int __acpi_power_on(struct acpi_power_resource *resource) { - int result = 0; - int found = 0; acpi_status status = AE_OK; - struct acpi_power_resource *resource = NULL; - struct list_head *node, *next; - struct acpi_power_reference *ref; + status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL); + if (ACPI_FAILURE(status)) + return -ENODEV; + + /* Update the power resource's _device_ power state */ + resource->device->power.state = ACPI_STATE_D0; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Power resource [%s] turned on\n", + resource->name)); + + return 0; +} + +static int acpi_power_on(acpi_handle handle) +{ + int result = 0; + struct acpi_power_resource *resource = NULL; result = acpi_power_get_context(handle, &resource); if (result) return result; mutex_lock(&resource->resource_lock); - list_for_each_safe(node, next, &resource->reference) { - ref = container_of(node, struct acpi_power_reference, node); - if (dev->handle == ref->device->handle) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] already referenced by resource [%s]\n", - dev->pnp.bus_id, resource->name)); - found = 1; - break; - } - } - if (!found) { - ref = kmalloc(sizeof (struct acpi_power_reference), - irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL); - if (!ref) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "kmalloc() failed\n")); - mutex_unlock(&resource->resource_lock); - return -ENOMEM; - } - list_add_tail(&ref->node, &resource->reference); - ref->device = dev; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] added to resource [%s] references\n", - dev->pnp.bus_id, resource->name)); + if (resource->ref_count++) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Power resource [%s] already on", + resource->name)); + } else { + result = __acpi_power_on(resource); } - mutex_unlock(&resource->resource_lock); - status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL); - if (ACPI_FAILURE(status)) - return -ENODEV; - - /* Update the power resource's _device_ power state */ - resource->device->power.state = ACPI_STATE_D0; + mutex_unlock(&resource->resource_lock); - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned on\n", - resource->name)); return 0; } -static int acpi_power_off_device(acpi_handle handle, struct acpi_device *dev) +static int acpi_power_off_device(acpi_handle handle) { int result = 0; acpi_status status = AE_OK; struct acpi_power_resource *resource = NULL; - struct list_head *node, *next; - struct acpi_power_reference *ref; result = acpi_power_get_context(handle, &resource); if (result) return result; mutex_lock(&resource->resource_lock); - list_for_each_safe(node, next, &resource->reference) { - ref = container_of(node, struct acpi_power_reference, node); - if (dev->handle == ref->device->handle) { - list_del(&ref->node); - kfree(ref); - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] removed from resource [%s] references\n", - dev->pnp.bus_id, resource->name)); - break; - } + + if (!resource->ref_count) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Power resource [%s] already off", + resource->name)); + goto unlock; } - if (!list_empty(&resource->reference)) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cannot turn resource [%s] off - resource is in use\n", - resource->name)); - mutex_unlock(&resource->resource_lock); - return 0; + if (--resource->ref_count) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Power resource [%s] still in use\n", + resource->name)); + goto unlock; } - mutex_unlock(&resource->resource_lock); status = acpi_evaluate_object(resource->device->handle, "_OFF", NULL, NULL); - if (ACPI_FAILURE(status)) - return -ENODEV; + if (ACPI_FAILURE(status)) { + result = -ENODEV; + } else { + /* Update the power resource's _device_ power state */ + resource->device->power.state = ACPI_STATE_D3; - /* Update the power resource's _device_ power state */ - resource->device->power.state = ACPI_STATE_D3; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Power resource [%s] turned off\n", + resource->name)); + } - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned off\n", - resource->name)); + unlock: + mutex_unlock(&resource->resource_lock); - return 0; + return result; } /** @@ -364,7 +347,7 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state) /* Open power resource */ for (i = 0; i < dev->wakeup.resources.count; i++) { - int ret = acpi_power_on(dev->wakeup.resources.handles[i], dev); + int ret = acpi_power_on(dev->wakeup.resources.handles[i]); if (ret) { printk(KERN_ERR PREFIX "Transition power state\n"); dev->wakeup.flags.valid = 0; @@ -420,7 +403,7 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev) /* Close power resource */ for (i = 0; i < dev->wakeup.resources.count; i++) { int ret = acpi_power_off_device( - dev->wakeup.resources.handles[i], dev); + dev->wakeup.resources.handles[i]); if (ret) { printk(KERN_ERR PREFIX "Transition power state\n"); dev->wakeup.flags.valid = 0; @@ -500,7 +483,7 @@ int acpi_power_transition(struct acpi_device *device, int state) * (e.g. so the device doesn't lose power while transitioning). */ for (i = 0; i < tl->count; i++) { - result = acpi_power_on(tl->handles[i], device); + result = acpi_power_on(tl->handles[i]); if (result) goto end; } @@ -513,7 +496,7 @@ int acpi_power_transition(struct acpi_device *device, int state) * Then we dereference all power resources used in the current list. */ for (i = 0; i < cl->count; i++) { - result = acpi_power_off_device(cl->handles[i], device); + result = acpi_power_off_device(cl->handles[i]); if (result) goto end; } @@ -551,7 +534,6 @@ static int acpi_power_add(struct acpi_device *device) resource->device = device; mutex_init(&resource->resource_lock); - INIT_LIST_HEAD(&resource->reference); strcpy(resource->name, device->pnp.bus_id); strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_POWER_CLASS); @@ -594,22 +576,14 @@ static int acpi_power_add(struct acpi_device *device) static int acpi_power_remove(struct acpi_device *device, int type) { - struct acpi_power_resource *resource = NULL; - struct list_head *node, *next; + struct acpi_power_resource *resource; - - if (!device || !acpi_driver_data(device)) + if (!device) return -EINVAL; resource = acpi_driver_data(device); - - mutex_lock(&resource->resource_lock); - list_for_each_safe(node, next, &resource->reference) { - struct acpi_power_reference *ref = container_of(node, struct acpi_power_reference, node); - list_del(&ref->node); - kfree(ref); - } - mutex_unlock(&resource->resource_lock); + if (!resource) + return -EINVAL; kfree(resource); @@ -619,29 +593,28 @@ static int acpi_power_remove(struct acpi_device *device, int type) static int acpi_power_resume(struct acpi_device *device) { int result = 0, state; - struct acpi_power_resource *resource = NULL; - struct acpi_power_reference *ref; + struct acpi_power_resource *resource; - if (!device || !acpi_driver_data(device)) + if (!device) return -EINVAL; resource = acpi_driver_data(device); + if (!resource) + return -EINVAL; + + mutex_lock(&resource->resource_lock); result = acpi_power_get_state(device->handle, &state); if (result) - return result; + goto unlock; - mutex_lock(&resource->resource_lock); - if (state == ACPI_POWER_RESOURCE_STATE_OFF && - !list_empty(&resource->reference)) { - ref = container_of(resource->reference.next, struct acpi_power_reference, node); - mutex_unlock(&resource->resource_lock); - result = acpi_power_on(device->handle, ref->device); - return result; - } + if (state == ACPI_POWER_RESOURCE_STATE_OFF && resource->ref_count) + result = __acpi_power_on(resource); + unlock: mutex_unlock(&resource->resource_lock); - return 0; + + return result; } int __init acpi_power_init(void) |