mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	driver core: Add device link support for SYNC_STATE_ONLY flag
Parent devices might need to create "proxy" device links from themselves to supplier devices to make sure the supplier devices don't get a sync_state() before the child consumer devices get a chance to add device links to the supplier devices. However, the parent device has no real dependency on the supplier device and probing, suspend/resume or runtime PM don't need to be affected by the supplier device. To capture these cases, create a SYNC_STATE_ONLY device link flag that only affects sync_state() behavior and doesn't affect probing, suspend/resume or runtime PM. Signed-off-by: Saravana Kannan <saravanak@google.com> Link: https://lore.kernel.org/r/20191028220027.251605-2-saravanak@google.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
		
							parent
							
								
									8f677bc819
								
							
						
					
					
						commit
						05ef983e0d
					
				
					 2 changed files with 43 additions and 9 deletions
				
			
		| 
						 | 
					@ -131,6 +131,9 @@ static int device_is_dependent(struct device *dev, void *target)
 | 
				
			||||||
		return ret;
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	list_for_each_entry(link, &dev->links.consumers, s_node) {
 | 
						list_for_each_entry(link, &dev->links.consumers, s_node) {
 | 
				
			||||||
 | 
							if (link->flags == (DL_FLAG_SYNC_STATE_ONLY | DL_FLAG_MANAGED))
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (link->consumer == target)
 | 
							if (link->consumer == target)
 | 
				
			||||||
			return 1;
 | 
								return 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -200,8 +203,11 @@ static int device_reorder_to_tail(struct device *dev, void *not_used)
 | 
				
			||||||
		device_pm_move_last(dev);
 | 
							device_pm_move_last(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	device_for_each_child(dev, NULL, device_reorder_to_tail);
 | 
						device_for_each_child(dev, NULL, device_reorder_to_tail);
 | 
				
			||||||
	list_for_each_entry(link, &dev->links.consumers, s_node)
 | 
						list_for_each_entry(link, &dev->links.consumers, s_node) {
 | 
				
			||||||
 | 
							if (link->flags == (DL_FLAG_SYNC_STATE_ONLY | DL_FLAG_MANAGED))
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
		device_reorder_to_tail(link->consumer, NULL);
 | 
							device_reorder_to_tail(link->consumer, NULL);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -228,7 +234,8 @@ void device_pm_move_to_tail(struct device *dev)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define DL_MANAGED_LINK_FLAGS (DL_FLAG_AUTOREMOVE_CONSUMER | \
 | 
					#define DL_MANAGED_LINK_FLAGS (DL_FLAG_AUTOREMOVE_CONSUMER | \
 | 
				
			||||||
			       DL_FLAG_AUTOREMOVE_SUPPLIER | \
 | 
								       DL_FLAG_AUTOREMOVE_SUPPLIER | \
 | 
				
			||||||
			       DL_FLAG_AUTOPROBE_CONSUMER)
 | 
								       DL_FLAG_AUTOPROBE_CONSUMER  | \
 | 
				
			||||||
 | 
								       DL_FLAG_SYNC_STATE_ONLY)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define DL_ADD_VALID_FLAGS (DL_MANAGED_LINK_FLAGS | DL_FLAG_STATELESS | \
 | 
					#define DL_ADD_VALID_FLAGS (DL_MANAGED_LINK_FLAGS | DL_FLAG_STATELESS | \
 | 
				
			||||||
			    DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE)
 | 
								    DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE)
 | 
				
			||||||
| 
						 | 
					@ -296,6 +303,8 @@ struct device_link *device_link_add(struct device *consumer,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!consumer || !supplier || flags & ~DL_ADD_VALID_FLAGS ||
 | 
						if (!consumer || !supplier || flags & ~DL_ADD_VALID_FLAGS ||
 | 
				
			||||||
	    (flags & DL_FLAG_STATELESS && flags & DL_MANAGED_LINK_FLAGS) ||
 | 
						    (flags & DL_FLAG_STATELESS && flags & DL_MANAGED_LINK_FLAGS) ||
 | 
				
			||||||
 | 
						    (flags & DL_FLAG_SYNC_STATE_ONLY &&
 | 
				
			||||||
 | 
						     flags != DL_FLAG_SYNC_STATE_ONLY) ||
 | 
				
			||||||
	    (flags & DL_FLAG_AUTOPROBE_CONSUMER &&
 | 
						    (flags & DL_FLAG_AUTOPROBE_CONSUMER &&
 | 
				
			||||||
	     flags & (DL_FLAG_AUTOREMOVE_CONSUMER |
 | 
						     flags & (DL_FLAG_AUTOREMOVE_CONSUMER |
 | 
				
			||||||
		      DL_FLAG_AUTOREMOVE_SUPPLIER)))
 | 
							      DL_FLAG_AUTOREMOVE_SUPPLIER)))
 | 
				
			||||||
| 
						 | 
					@ -316,11 +325,14 @@ struct device_link *device_link_add(struct device *consumer,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * If the supplier has not been fully registered yet or there is a
 | 
						 * If the supplier has not been fully registered yet or there is a
 | 
				
			||||||
	 * reverse dependency between the consumer and the supplier already in
 | 
						 * reverse (non-SYNC_STATE_ONLY) dependency between the consumer and
 | 
				
			||||||
	 * the graph, return NULL.
 | 
						 * the supplier already in the graph, return NULL. If the link is a
 | 
				
			||||||
 | 
						 * SYNC_STATE_ONLY link, we don't check for reverse dependencies
 | 
				
			||||||
 | 
						 * because it only affects sync_state() callbacks.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (!device_pm_initialized(supplier)
 | 
						if (!device_pm_initialized(supplier)
 | 
				
			||||||
	    || device_is_dependent(consumer, supplier)) {
 | 
						    || (!(flags & DL_FLAG_SYNC_STATE_ONLY) &&
 | 
				
			||||||
 | 
							  device_is_dependent(consumer, supplier))) {
 | 
				
			||||||
		link = NULL;
 | 
							link = NULL;
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -347,9 +359,14 @@ struct device_link *device_link_add(struct device *consumer,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (flags & DL_FLAG_STATELESS) {
 | 
							if (flags & DL_FLAG_STATELESS) {
 | 
				
			||||||
			link->flags |= DL_FLAG_STATELESS;
 | 
					 | 
				
			||||||
			kref_get(&link->kref);
 | 
								kref_get(&link->kref);
 | 
				
			||||||
			goto out;
 | 
								if (link->flags & DL_FLAG_SYNC_STATE_ONLY &&
 | 
				
			||||||
 | 
								    !(link->flags & DL_FLAG_STATELESS)) {
 | 
				
			||||||
 | 
									link->flags |= DL_FLAG_STATELESS;
 | 
				
			||||||
 | 
									goto reorder;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									goto out;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
| 
						 | 
					@ -371,6 +388,12 @@ struct device_link *device_link_add(struct device *consumer,
 | 
				
			||||||
			link->flags |= DL_FLAG_MANAGED;
 | 
								link->flags |= DL_FLAG_MANAGED;
 | 
				
			||||||
			device_link_init_status(link, consumer, supplier);
 | 
								device_link_init_status(link, consumer, supplier);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							if (link->flags & DL_FLAG_SYNC_STATE_ONLY &&
 | 
				
			||||||
 | 
							    !(flags & DL_FLAG_SYNC_STATE_ONLY)) {
 | 
				
			||||||
 | 
								link->flags &= ~DL_FLAG_SYNC_STATE_ONLY;
 | 
				
			||||||
 | 
								goto reorder;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -410,6 +433,13 @@ struct device_link *device_link_add(struct device *consumer,
 | 
				
			||||||
	    flags & DL_FLAG_PM_RUNTIME)
 | 
						    flags & DL_FLAG_PM_RUNTIME)
 | 
				
			||||||
		pm_runtime_resume(supplier);
 | 
							pm_runtime_resume(supplier);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (flags & DL_FLAG_SYNC_STATE_ONLY) {
 | 
				
			||||||
 | 
							dev_dbg(consumer,
 | 
				
			||||||
 | 
								"Linked as a sync state only consumer to %s\n",
 | 
				
			||||||
 | 
								dev_name(supplier));
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					reorder:
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Move the consumer and all of the devices depending on it to the end
 | 
						 * Move the consumer and all of the devices depending on it to the end
 | 
				
			||||||
	 * of dpm_list and the devices_kset list.
 | 
						 * of dpm_list and the devices_kset list.
 | 
				
			||||||
| 
						 | 
					@ -635,7 +665,8 @@ int device_links_check_suppliers(struct device *dev)
 | 
				
			||||||
	device_links_write_lock();
 | 
						device_links_write_lock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	list_for_each_entry(link, &dev->links.suppliers, c_node) {
 | 
						list_for_each_entry(link, &dev->links.suppliers, c_node) {
 | 
				
			||||||
		if (!(link->flags & DL_FLAG_MANAGED))
 | 
							if (!(link->flags & DL_FLAG_MANAGED) ||
 | 
				
			||||||
 | 
							    link->flags & DL_FLAG_SYNC_STATE_ONLY)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (link->status != DL_STATE_AVAILABLE) {
 | 
							if (link->status != DL_STATE_AVAILABLE) {
 | 
				
			||||||
| 
						 | 
					@ -949,7 +980,8 @@ void device_links_unbind_consumers(struct device *dev)
 | 
				
			||||||
	list_for_each_entry(link, &dev->links.consumers, s_node) {
 | 
						list_for_each_entry(link, &dev->links.consumers, s_node) {
 | 
				
			||||||
		enum device_link_state status;
 | 
							enum device_link_state status;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!(link->flags & DL_FLAG_MANAGED))
 | 
							if (!(link->flags & DL_FLAG_MANAGED) ||
 | 
				
			||||||
 | 
							    link->flags & DL_FLAG_SYNC_STATE_ONLY)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		status = link->status;
 | 
							status = link->status;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1096,6 +1096,7 @@ enum device_link_state {
 | 
				
			||||||
 * AUTOREMOVE_SUPPLIER: Remove the link automatically on supplier driver unbind.
 | 
					 * AUTOREMOVE_SUPPLIER: Remove the link automatically on supplier driver unbind.
 | 
				
			||||||
 * AUTOPROBE_CONSUMER: Probe consumer driver automatically after supplier binds.
 | 
					 * AUTOPROBE_CONSUMER: Probe consumer driver automatically after supplier binds.
 | 
				
			||||||
 * MANAGED: The core tracks presence of supplier/consumer drivers (internal).
 | 
					 * MANAGED: The core tracks presence of supplier/consumer drivers (internal).
 | 
				
			||||||
 | 
					 * SYNC_STATE_ONLY: Link only affects sync_state() behavior.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
#define DL_FLAG_STATELESS		BIT(0)
 | 
					#define DL_FLAG_STATELESS		BIT(0)
 | 
				
			||||||
#define DL_FLAG_AUTOREMOVE_CONSUMER	BIT(1)
 | 
					#define DL_FLAG_AUTOREMOVE_CONSUMER	BIT(1)
 | 
				
			||||||
| 
						 | 
					@ -1104,6 +1105,7 @@ enum device_link_state {
 | 
				
			||||||
#define DL_FLAG_AUTOREMOVE_SUPPLIER	BIT(4)
 | 
					#define DL_FLAG_AUTOREMOVE_SUPPLIER	BIT(4)
 | 
				
			||||||
#define DL_FLAG_AUTOPROBE_CONSUMER	BIT(5)
 | 
					#define DL_FLAG_AUTOPROBE_CONSUMER	BIT(5)
 | 
				
			||||||
#define DL_FLAG_MANAGED			BIT(6)
 | 
					#define DL_FLAG_MANAGED			BIT(6)
 | 
				
			||||||
 | 
					#define DL_FLAG_SYNC_STATE_ONLY		BIT(7)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * struct device_link - Device link representation.
 | 
					 * struct device_link - Device link representation.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue