diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2012-11-21 00:21:39 +0100 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2012-11-21 00:21:39 +0100 |
commit | f3fd0c8a7fc1e4f3107a09a75e622781d3007b56 (patch) | |
tree | d63af1d6106032a1fdbc788ef9b7805f01a05a0a /drivers/acpi | |
parent | 8a66790b7850a6669129af078768a1d42076a0ef (diff) |
ACPI: Allow ACPI handles of devices to be initialized in advance
Currently, the ACPI handles of devices are initialized from within
device_add(), by acpi_bind_one() called from acpi_platform_notify()
which first uses the .find_device() routine provided by the device's
bus type to find the matching device node in the ACPI namespace.
This is a source of some computational overhead and, moreover, the
correctness of the result depends on the implementation of
.find_device() which is known to fail occasionally for some bus types
(e.g. PCI). In some cases, however, the corresponding ACPI device
node is known already before calling device_add() for the given
struct device object and the whole .find_device() dance in
acpi_platform_notify() is then simply unnecessary.
For this reason, make it possible to initialize the ACPI handles of
devices before calling device_add() for them. Modify
acpi_platform_notify() to call acpi_bind_one() in advance to check
the device's existing ACPI handle and skip the .find_device()
search if that is successful. Change acpi_bind_one() accordingly.
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/glue.c | 44 |
1 files changed, 34 insertions, 10 deletions
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 2f3849aedc97..3e75d6e5a469 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -130,46 +130,59 @@ static int acpi_bind_one(struct device *dev, acpi_handle handle) { struct acpi_device *acpi_dev; acpi_status status; - struct acpi_device_physical_node *physical_node; + struct acpi_device_physical_node *physical_node, *pn; char physical_node_name[sizeof(PHYSICAL_NODE_STRING) + 2]; int retval = -EINVAL; if (dev->acpi_handle) { - dev_warn(dev, "Drivers changed 'acpi_handle'\n"); - return -EINVAL; + if (handle) { + dev_warn(dev, "ACPI handle is already set\n"); + return -EINVAL; + } else { + handle = dev->acpi_handle; + } } + if (!handle) + return -EINVAL; get_device(dev); status = acpi_bus_get_device(handle, &acpi_dev); if (ACPI_FAILURE(status)) goto err; - physical_node = kzalloc(sizeof(struct acpi_device_physical_node), - GFP_KERNEL); + physical_node = kzalloc(sizeof(*physical_node), GFP_KERNEL); if (!physical_node) { retval = -ENOMEM; goto err; } mutex_lock(&acpi_dev->physical_node_lock); + + /* Sanity check. */ + list_for_each_entry(pn, &acpi_dev->physical_node_list, node) + if (pn->dev == dev) { + dev_warn(dev, "Already associated with ACPI node\n"); + goto err_free; + } + /* allocate physical node id according to physical_node_id_bitmap */ physical_node->node_id = find_first_zero_bit(acpi_dev->physical_node_id_bitmap, ACPI_MAX_PHYSICAL_NODE); if (physical_node->node_id >= ACPI_MAX_PHYSICAL_NODE) { retval = -ENOSPC; - mutex_unlock(&acpi_dev->physical_node_lock); - kfree(physical_node); - goto err; + goto err_free; } set_bit(physical_node->node_id, acpi_dev->physical_node_id_bitmap); physical_node->dev = dev; list_add_tail(&physical_node->node, &acpi_dev->physical_node_list); acpi_dev->physical_node_count++; + mutex_unlock(&acpi_dev->physical_node_lock); - dev->acpi_handle = handle; + if (!dev->acpi_handle) + dev->acpi_handle = handle; if (!physical_node->node_id) strcpy(physical_node_name, PHYSICAL_NODE_STRING); @@ -187,8 +200,14 @@ static int acpi_bind_one(struct device *dev, acpi_handle handle) return 0; err: + dev->acpi_handle = NULL; put_device(dev); return retval; + + err_free: + mutex_unlock(&acpi_dev->physical_node_lock); + kfree(physical_node); + goto err; } static int acpi_unbind_one(struct device *dev) @@ -247,6 +266,10 @@ static int acpi_platform_notify(struct device *dev) acpi_handle handle; int ret = -EINVAL; + ret = acpi_bind_one(dev, NULL); + if (!ret) + goto out; + if (!dev->bus || !dev->parent) { /* bridge devices genernally haven't bus or parent */ ret = acpi_find_bridge_device(dev, &handle); @@ -260,10 +283,11 @@ static int acpi_platform_notify(struct device *dev) } if ((ret = type->find_device(dev, &handle)) != 0) DBG("Can't get handler for %s\n", dev_name(dev)); - end: + end: if (!ret) acpi_bind_one(dev, handle); + out: #if ACPI_GLUE_DEBUG if (!ret) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; |