Commit f265df55 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki Committed by Greg Kroah-Hartman

driver core: Avoid careless re-use of existing device links

After commit ead18c23 ("driver core: Introduce device links
reference counting"), if there is a link between the given supplier
and the given consumer already, device_link_add() will refcount it
and return it unconditionally.  However, if the flags passed to
it on the second (or any subsequent) attempt to create a device
link between the same consumer-supplier pair are not compatible with
the existing link's flags, that is incorrect.

First off, if the existing link is stateless and the next caller of
device_link_add() for the same consumer-supplier pair wants a
stateful one, or the other way around, the existing link cannot be
returned, because it will not match the expected behavior, so make
device_link_add() dump the stack and return NULL in that case.

Moreover, if the DL_FLAG_AUTOREMOVE_CONSUMER flag is passed to
device_link_add(), its caller will expect its reference to the link
to be dropped automatically on consumer driver removal, which will
not happen if that flag is not set in the link's flags (and
analogously for DL_FLAG_AUTOREMOVE_SUPPLIER).  For this reason, make
device_link_add() update the existing link's flags accordingly
before returning it to the caller.

Fixes: ead18c23 ("driver core: Introduce device links reference counting")
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent c8d50986
...@@ -221,12 +221,29 @@ struct device_link *device_link_add(struct device *consumer, ...@@ -221,12 +221,29 @@ struct device_link *device_link_add(struct device *consumer,
goto out; goto out;
} }
list_for_each_entry(link, &supplier->links.consumers, s_node) list_for_each_entry(link, &supplier->links.consumers, s_node) {
if (link->consumer == consumer) { if (link->consumer != consumer)
kref_get(&link->kref); 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; goto out;
} }
if (flags & DL_FLAG_AUTOREMOVE_CONSUMER)
link->flags |= DL_FLAG_AUTOREMOVE_CONSUMER;
if (flags & DL_FLAG_AUTOREMOVE_SUPPLIER)
link->flags |= DL_FLAG_AUTOREMOVE_SUPPLIER;
kref_get(&link->kref);
goto out;
}
link = kzalloc(sizeof(*link), GFP_KERNEL); link = kzalloc(sizeof(*link), GFP_KERNEL);
if (!link) if (!link)
goto out; goto out;
......
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