|
@@ -196,8 +196,10 @@ struct device_link *device_link_add(struct device *consumer,
|
|
|
}
|
|
|
|
|
|
list_for_each_entry(link, &supplier->links.consumers, s_node)
|
|
|
- if (link->consumer == consumer)
|
|
|
+ if (link->consumer == consumer) {
|
|
|
+ kref_get(&link->kref);
|
|
|
goto out;
|
|
|
+ }
|
|
|
|
|
|
link = kzalloc(sizeof(*link), GFP_KERNEL);
|
|
|
if (!link)
|
|
@@ -222,6 +224,7 @@ struct device_link *device_link_add(struct device *consumer,
|
|
|
link->consumer = consumer;
|
|
|
INIT_LIST_HEAD(&link->c_node);
|
|
|
link->flags = flags;
|
|
|
+ kref_init(&link->kref);
|
|
|
|
|
|
/* Determine the initial link state. */
|
|
|
if (flags & DL_FLAG_STATELESS) {
|
|
@@ -292,8 +295,10 @@ static void __device_link_free_srcu(struct rcu_head *rhead)
|
|
|
device_link_free(container_of(rhead, struct device_link, rcu_head));
|
|
|
}
|
|
|
|
|
|
-static void __device_link_del(struct device_link *link)
|
|
|
+static void __device_link_del(struct kref *kref)
|
|
|
{
|
|
|
+ struct device_link *link = container_of(kref, struct device_link, kref);
|
|
|
+
|
|
|
dev_info(link->consumer, "Dropping the link to %s\n",
|
|
|
dev_name(link->supplier));
|
|
|
|
|
@@ -305,8 +310,10 @@ static void __device_link_del(struct device_link *link)
|
|
|
call_srcu(&device_links_srcu, &link->rcu_head, __device_link_free_srcu);
|
|
|
}
|
|
|
#else /* !CONFIG_SRCU */
|
|
|
-static void __device_link_del(struct device_link *link)
|
|
|
+static void __device_link_del(struct kref *kref)
|
|
|
{
|
|
|
+ struct device_link *link = container_of(kref, struct device_link, kref);
|
|
|
+
|
|
|
dev_info(link->consumer, "Dropping the link to %s\n",
|
|
|
dev_name(link->supplier));
|
|
|
|
|
@@ -324,13 +331,15 @@ static void __device_link_del(struct device_link *link)
|
|
|
* @link: Device link to delete.
|
|
|
*
|
|
|
* The caller must ensure proper synchronization of this function with runtime
|
|
|
- * PM.
|
|
|
+ * PM. If the link was added multiple times, it needs to be deleted as often.
|
|
|
+ * Care is required for hotplugged devices: Their links are purged on removal
|
|
|
+ * and calling device_link_del() is then no longer allowed.
|
|
|
*/
|
|
|
void device_link_del(struct device_link *link)
|
|
|
{
|
|
|
device_links_write_lock();
|
|
|
device_pm_lock();
|
|
|
- __device_link_del(link);
|
|
|
+ kref_put(&link->kref, __device_link_del);
|
|
|
device_pm_unlock();
|
|
|
device_links_write_unlock();
|
|
|
}
|
|
@@ -444,7 +453,7 @@ static void __device_links_no_driver(struct device *dev)
|
|
|
continue;
|
|
|
|
|
|
if (link->flags & DL_FLAG_AUTOREMOVE)
|
|
|
- __device_link_del(link);
|
|
|
+ kref_put(&link->kref, __device_link_del);
|
|
|
else if (link->status != DL_STATE_SUPPLIER_UNBIND)
|
|
|
WRITE_ONCE(link->status, DL_STATE_AVAILABLE);
|
|
|
}
|
|
@@ -597,13 +606,13 @@ static void device_links_purge(struct device *dev)
|
|
|
|
|
|
list_for_each_entry_safe_reverse(link, ln, &dev->links.suppliers, c_node) {
|
|
|
WARN_ON(link->status == DL_STATE_ACTIVE);
|
|
|
- __device_link_del(link);
|
|
|
+ __device_link_del(&link->kref);
|
|
|
}
|
|
|
|
|
|
list_for_each_entry_safe_reverse(link, ln, &dev->links.consumers, s_node) {
|
|
|
WARN_ON(link->status != DL_STATE_DORMANT &&
|
|
|
link->status != DL_STATE_NONE);
|
|
|
- __device_link_del(link);
|
|
|
+ __device_link_del(&link->kref);
|
|
|
}
|
|
|
|
|
|
device_links_write_unlock();
|