diff options
Diffstat (limited to 'drivers/base/core.c')
| -rw-r--r-- | drivers/base/core.c | 231 | 
1 files changed, 145 insertions, 86 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c index 1669d41fcddc..2db62d98e395 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -68,6 +68,11 @@ void device_links_read_unlock(int idx)  {  	srcu_read_unlock(&device_links_srcu, idx);  } + +int device_links_read_lock_held(void) +{ +	return srcu_read_lock_held(&device_links_srcu); +}  #else /* !CONFIG_SRCU */  static DECLARE_RWSEM(device_links_lock); @@ -91,6 +96,13 @@ void device_links_read_unlock(int not_used)  {  	up_read(&device_links_lock);  } + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +int device_links_read_lock_held(void) +{ +	return lockdep_is_held(&device_links_lock); +} +#endif  #endif /* !CONFIG_SRCU */  /** @@ -124,6 +136,50 @@ static int device_is_dependent(struct device *dev, void *target)  	return ret;  } +static void device_link_init_status(struct device_link *link, +				    struct device *consumer, +				    struct device *supplier) +{ +	switch (supplier->links.status) { +	case DL_DEV_PROBING: +		switch (consumer->links.status) { +		case DL_DEV_PROBING: +			/* +			 * A consumer driver can create a link to a supplier +			 * that has not completed its probing yet as long as it +			 * knows that the supplier is already functional (for +			 * example, it has just acquired some resources from the +			 * supplier). +			 */ +			link->status = DL_STATE_CONSUMER_PROBE; +			break; +		default: +			link->status = DL_STATE_DORMANT; +			break; +		} +		break; +	case DL_DEV_DRIVER_BOUND: +		switch (consumer->links.status) { +		case DL_DEV_PROBING: +			link->status = DL_STATE_CONSUMER_PROBE; +			break; +		case DL_DEV_DRIVER_BOUND: +			link->status = DL_STATE_ACTIVE; +			break; +		default: +			link->status = DL_STATE_AVAILABLE; +			break; +		} +		break; +	case DL_DEV_UNBINDING: +		link->status = DL_STATE_SUPPLIER_UNBIND; +		break; +	default: +		link->status = DL_STATE_DORMANT; +		break; +	} +} +  static int device_reorder_to_tail(struct device *dev, void *not_used)  {  	struct device_link *link; @@ -165,6 +221,13 @@ void device_pm_move_to_tail(struct device *dev)  	device_links_read_unlock(idx);  } +#define DL_MANAGED_LINK_FLAGS (DL_FLAG_AUTOREMOVE_CONSUMER | \ +			       DL_FLAG_AUTOREMOVE_SUPPLIER | \ +			       DL_FLAG_AUTOPROBE_CONSUMER) + +#define DL_ADD_VALID_FLAGS (DL_MANAGED_LINK_FLAGS | DL_FLAG_STATELESS | \ +			    DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE) +  /**   * device_link_add - Create a link between two devices.   * @consumer: Consumer end of the link. @@ -179,9 +242,9 @@ void device_pm_move_to_tail(struct device *dev)   * of the link.  If DL_FLAG_PM_RUNTIME is not set, DL_FLAG_RPM_ACTIVE will be   * ignored.   * - * If DL_FLAG_STATELESS is set in @flags, the link is not going to be managed by - * the driver core and, in particular, the caller of this function is expected - * to drop the reference to the link acquired by it directly. + * If DL_FLAG_STATELESS is set in @flags, the caller of this function is + * expected to release the link returned by it directly with the help of either + * device_link_del() or device_link_remove().   *   * If that flag is not set, however, the caller of this function is handing the   * management of the link over to the driver core entirely and its return value @@ -201,9 +264,16 @@ void device_pm_move_to_tail(struct device *dev)   * be used to request the driver core to automaticall probe for a consmer   * driver after successfully binding a driver to the supplier device.   * - * The combination of DL_FLAG_STATELESS and either DL_FLAG_AUTOREMOVE_CONSUMER - * or DL_FLAG_AUTOREMOVE_SUPPLIER set in @flags at the same time is invalid and - * will cause NULL to be returned upfront. + * The combination of DL_FLAG_STATELESS and one of DL_FLAG_AUTOREMOVE_CONSUMER, + * DL_FLAG_AUTOREMOVE_SUPPLIER, or DL_FLAG_AUTOPROBE_CONSUMER set in @flags at + * the same time is invalid and will cause NULL to be returned upfront. + * However, if a device link between the given @consumer and @supplier pair + * exists already when this function is called for them, the existing link will + * be returned regardless of its current type and status (the link's flags may + * be modified then).  The caller of this function is then expected to treat + * the link as though it has just been created, so (in particular) if + * DL_FLAG_STATELESS was passed in @flags, the link needs to be released + * explicitly when not needed any more (as stated above).   *   * A side effect of the link creation is re-ordering of dpm_list and the   * devices_kset list by moving the consumer device and all devices depending @@ -219,11 +289,8 @@ struct device_link *device_link_add(struct device *consumer,  {  	struct device_link *link; -	if (!consumer || !supplier || -	    (flags & DL_FLAG_STATELESS && -	     flags & (DL_FLAG_AUTOREMOVE_CONSUMER | -		      DL_FLAG_AUTOREMOVE_SUPPLIER | -		      DL_FLAG_AUTOPROBE_CONSUMER)) || +	if (!consumer || !supplier || flags & ~DL_ADD_VALID_FLAGS || +	    (flags & DL_FLAG_STATELESS && flags & DL_MANAGED_LINK_FLAGS) ||  	    (flags & DL_FLAG_AUTOPROBE_CONSUMER &&  	     flags & (DL_FLAG_AUTOREMOVE_CONSUMER |  		      DL_FLAG_AUTOREMOVE_SUPPLIER))) @@ -236,6 +303,9 @@ struct device_link *device_link_add(struct device *consumer,  		}  	} +	if (!(flags & DL_FLAG_STATELESS)) +		flags |= DL_FLAG_MANAGED; +  	device_links_write_lock();  	device_pm_lock(); @@ -262,15 +332,6 @@ struct device_link *device_link_add(struct device *consumer,  		if (link->consumer != consumer)  			continue; -		/* -		 * Don't return a stateless link if the caller wants a stateful -		 * one and vice versa. -		 */ -		if (WARN_ON((flags & DL_FLAG_STATELESS) != (link->flags & DL_FLAG_STATELESS))) { -			link = NULL; -			goto out; -		} -  		if (flags & DL_FLAG_PM_RUNTIME) {  			if (!(link->flags & DL_FLAG_PM_RUNTIME)) {  				pm_runtime_new_link(consumer); @@ -281,6 +342,7 @@ struct device_link *device_link_add(struct device *consumer,  		}  		if (flags & DL_FLAG_STATELESS) { +			link->flags |= DL_FLAG_STATELESS;  			kref_get(&link->kref);  			goto out;  		} @@ -299,6 +361,11 @@ struct device_link *device_link_add(struct device *consumer,  			link->flags &= ~(DL_FLAG_AUTOREMOVE_CONSUMER |  					 DL_FLAG_AUTOREMOVE_SUPPLIER);  		} +		if (!(link->flags & DL_FLAG_MANAGED)) { +			kref_get(&link->kref); +			link->flags |= DL_FLAG_MANAGED; +			device_link_init_status(link, consumer, supplier); +		}  		goto out;  	} @@ -325,48 +392,10 @@ struct device_link *device_link_add(struct device *consumer,  	kref_init(&link->kref);  	/* Determine the initial link state. */ -	if (flags & DL_FLAG_STATELESS) { +	if (flags & DL_FLAG_STATELESS)  		link->status = DL_STATE_NONE; -	} else { -		switch (supplier->links.status) { -		case DL_DEV_PROBING: -			switch (consumer->links.status) { -			case DL_DEV_PROBING: -				/* -				 * A consumer driver can create a link to a -				 * supplier that has not completed its probing -				 * yet as long as it knows that the supplier is -				 * already functional (for example, it has just -				 * acquired some resources from the supplier). -				 */ -				link->status = DL_STATE_CONSUMER_PROBE; -				break; -			default: -				link->status = DL_STATE_DORMANT; -				break; -			} -			break; -		case DL_DEV_DRIVER_BOUND: -			switch (consumer->links.status) { -			case DL_DEV_PROBING: -				link->status = DL_STATE_CONSUMER_PROBE; -				break; -			case DL_DEV_DRIVER_BOUND: -				link->status = DL_STATE_ACTIVE; -				break; -			default: -				link->status = DL_STATE_AVAILABLE; -				break; -			} -			break; -		case DL_DEV_UNBINDING: -			link->status = DL_STATE_SUPPLIER_UNBIND; -			break; -		default: -			link->status = DL_STATE_DORMANT; -			break; -		} -	} +	else +		device_link_init_status(link, consumer, supplier);  	/*  	 * Some callers expect the link creation during consumer driver probe to @@ -528,7 +557,7 @@ static void device_links_missing_supplier(struct device *dev)   * mark the link as "consumer probe in progress" to make the supplier removal   * wait for us to complete (or bad things may happen).   * - * Links with the DL_FLAG_STATELESS flag set are ignored. + * Links without the DL_FLAG_MANAGED flag set are ignored.   */  int device_links_check_suppliers(struct device *dev)  { @@ -538,7 +567,7 @@ int device_links_check_suppliers(struct device *dev)  	device_links_write_lock();  	list_for_each_entry(link, &dev->links.suppliers, c_node) { -		if (link->flags & DL_FLAG_STATELESS) +		if (!(link->flags & DL_FLAG_MANAGED))  			continue;  		if (link->status != DL_STATE_AVAILABLE) { @@ -563,7 +592,7 @@ int device_links_check_suppliers(struct device *dev)   *   * Also change the status of @dev's links to suppliers to "active".   * - * Links with the DL_FLAG_STATELESS flag set are ignored. + * Links without the DL_FLAG_MANAGED flag set are ignored.   */  void device_links_driver_bound(struct device *dev)  { @@ -572,7 +601,7 @@ void device_links_driver_bound(struct device *dev)  	device_links_write_lock();  	list_for_each_entry(link, &dev->links.consumers, s_node) { -		if (link->flags & DL_FLAG_STATELESS) +		if (!(link->flags & DL_FLAG_MANAGED))  			continue;  		/* @@ -593,7 +622,7 @@ void device_links_driver_bound(struct device *dev)  	}  	list_for_each_entry(link, &dev->links.suppliers, c_node) { -		if (link->flags & DL_FLAG_STATELESS) +		if (!(link->flags & DL_FLAG_MANAGED))  			continue;  		WARN_ON(link->status != DL_STATE_CONSUMER_PROBE); @@ -605,6 +634,13 @@ void device_links_driver_bound(struct device *dev)  	device_links_write_unlock();  } +static void device_link_drop_managed(struct device_link *link) +{ +	link->flags &= ~DL_FLAG_MANAGED; +	WRITE_ONCE(link->status, DL_STATE_NONE); +	kref_put(&link->kref, __device_link_del); +} +  /**   * __device_links_no_driver - Update links of a device without a driver.   * @dev: Device without a drvier. @@ -615,18 +651,18 @@ void device_links_driver_bound(struct device *dev)   * unless they already are in the "supplier unbind in progress" state in which   * case they need not be updated.   * - * Links with the DL_FLAG_STATELESS flag set are ignored. + * Links without the DL_FLAG_MANAGED flag set are ignored.   */  static void __device_links_no_driver(struct device *dev)  {  	struct device_link *link, *ln;  	list_for_each_entry_safe_reverse(link, ln, &dev->links.suppliers, c_node) { -		if (link->flags & DL_FLAG_STATELESS) +		if (!(link->flags & DL_FLAG_MANAGED))  			continue;  		if (link->flags & DL_FLAG_AUTOREMOVE_CONSUMER) -			__device_link_del(&link->kref); +			device_link_drop_managed(link);  		else if (link->status == DL_STATE_CONSUMER_PROBE ||  			 link->status == DL_STATE_ACTIVE)  			WRITE_ONCE(link->status, DL_STATE_AVAILABLE); @@ -643,7 +679,7 @@ static void __device_links_no_driver(struct device *dev)   * %__device_links_no_driver() to update links to suppliers for it as   * appropriate.   * - * Links with the DL_FLAG_STATELESS flag set are ignored. + * Links without the DL_FLAG_MANAGED flag set are ignored.   */  void device_links_no_driver(struct device *dev)  { @@ -652,7 +688,7 @@ void device_links_no_driver(struct device *dev)  	device_links_write_lock();  	list_for_each_entry(link, &dev->links.consumers, s_node) { -		if (link->flags & DL_FLAG_STATELESS) +		if (!(link->flags & DL_FLAG_MANAGED))  			continue;  		/* @@ -680,7 +716,7 @@ void device_links_no_driver(struct device *dev)   * invoke %__device_links_no_driver() to update links to suppliers for it as   * appropriate.   * - * Links with the DL_FLAG_STATELESS flag set are ignored. + * Links without the DL_FLAG_MANAGED flag set are ignored.   */  void device_links_driver_cleanup(struct device *dev)  { @@ -689,7 +725,7 @@ void device_links_driver_cleanup(struct device *dev)  	device_links_write_lock();  	list_for_each_entry_safe(link, ln, &dev->links.consumers, s_node) { -		if (link->flags & DL_FLAG_STATELESS) +		if (!(link->flags & DL_FLAG_MANAGED))  			continue;  		WARN_ON(link->flags & DL_FLAG_AUTOREMOVE_CONSUMER); @@ -702,7 +738,7 @@ void device_links_driver_cleanup(struct device *dev)  		 */  		if (link->status == DL_STATE_SUPPLIER_UNBIND &&  		    link->flags & DL_FLAG_AUTOREMOVE_SUPPLIER) -			__device_link_del(&link->kref); +			device_link_drop_managed(link);  		WRITE_ONCE(link->status, DL_STATE_DORMANT);  	} @@ -724,7 +760,7 @@ void device_links_driver_cleanup(struct device *dev)   *   * Return 'false' if there are no probing or active consumers.   * - * Links with the DL_FLAG_STATELESS flag set are ignored. + * Links without the DL_FLAG_MANAGED flag set are ignored.   */  bool device_links_busy(struct device *dev)  { @@ -734,7 +770,7 @@ bool device_links_busy(struct device *dev)  	device_links_write_lock();  	list_for_each_entry(link, &dev->links.consumers, s_node) { -		if (link->flags & DL_FLAG_STATELESS) +		if (!(link->flags & DL_FLAG_MANAGED))  			continue;  		if (link->status == DL_STATE_CONSUMER_PROBE @@ -764,7 +800,7 @@ bool device_links_busy(struct device *dev)   * driver to unbind and start over (the consumer will not re-probe as we have   * changed the state of the link already).   * - * Links with the DL_FLAG_STATELESS flag set are ignored. + * Links without the DL_FLAG_MANAGED flag set are ignored.   */  void device_links_unbind_consumers(struct device *dev)  { @@ -776,7 +812,7 @@ void device_links_unbind_consumers(struct device *dev)  	list_for_each_entry(link, &dev->links.consumers, s_node) {  		enum device_link_state status; -		if (link->flags & DL_FLAG_STATELESS) +		if (!(link->flags & DL_FLAG_MANAGED))  			continue;  		status = link->status; @@ -2932,13 +2968,6 @@ struct device *device_create_with_groups(struct class *class,  }  EXPORT_SYMBOL_GPL(device_create_with_groups); -static int __match_devt(struct device *dev, const void *data) -{ -	const dev_t *devt = data; - -	return dev->devt == *devt; -} -  /**   * device_destroy - removes a device that was created with device_create()   * @class: pointer to the struct class that this device was registered with @@ -2951,7 +2980,7 @@ void device_destroy(struct class *class, dev_t devt)  {  	struct device *dev; -	dev = class_find_device(class, NULL, &devt, __match_devt); +	dev = class_find_device_by_devt(class, devt);  	if (dev) {  		put_device(dev);  		device_unregister(dev); @@ -3422,8 +3451,38 @@ void device_set_of_node_from_dev(struct device *dev, const struct device *dev2)  }  EXPORT_SYMBOL_GPL(device_set_of_node_from_dev); +int device_match_name(struct device *dev, const void *name) +{ +	return sysfs_streq(dev_name(dev), name); +} +EXPORT_SYMBOL_GPL(device_match_name); +  int device_match_of_node(struct device *dev, const void *np)  {  	return dev->of_node == np;  }  EXPORT_SYMBOL_GPL(device_match_of_node); + +int device_match_fwnode(struct device *dev, const void *fwnode) +{ +	return dev_fwnode(dev) == fwnode; +} +EXPORT_SYMBOL_GPL(device_match_fwnode); + +int device_match_devt(struct device *dev, const void *pdevt) +{ +	return dev->devt == *(dev_t *)pdevt; +} +EXPORT_SYMBOL_GPL(device_match_devt); + +int device_match_acpi_dev(struct device *dev, const void *adev) +{ +	return ACPI_COMPANION(dev) == adev; +} +EXPORT_SYMBOL(device_match_acpi_dev); + +int device_match_any(struct device *dev, const void *unused) +{ +	return 1; +} +EXPORT_SYMBOL_GPL(device_match_any);  |