Commit 05ef983e authored by Saravana Kannan's avatar Saravana Kannan Committed by Greg Kroah-Hartman

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: default avatarSaravana Kannan <saravanak@google.com>
Link: https://lore.kernel.org/r/20191028220027.251605-2-saravanak@google.comSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 8f677bc8
...@@ -131,6 +131,9 @@ static int device_is_dependent(struct device *dev, void *target) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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 { ...@@ -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 { ...@@ -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.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment