forked from mirrors/linux
		
	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>
This commit is contained in:
		
							parent
							
								
									8a66790b78
								
							
						
					
					
						commit
						f3fd0c8a7f
					
				
					 1 changed files with 34 additions and 10 deletions
				
			
		| 
						 | 
				
			
			@ -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 };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue