diff options
Diffstat (limited to 'drivers/base/core.c')
-rw-r--r-- | drivers/base/core.c | 69 |
1 files changed, 54 insertions, 15 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c index 8c7327d45406..9d49b461b1d9 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c | |||
@@ -192,10 +192,21 @@ static void device_link_rpm_prepare(struct device *consumer, | |||
192 | * of the link. If DL_FLAG_PM_RUNTIME is not set, DL_FLAG_RPM_ACTIVE will be | 192 | * of the link. If DL_FLAG_PM_RUNTIME is not set, DL_FLAG_RPM_ACTIVE will be |
193 | * ignored. | 193 | * ignored. |
194 | * | 194 | * |
195 | * If the DL_FLAG_AUTOREMOVE_CONSUMER flag is set, the link will be removed | 195 | * If DL_FLAG_STATELESS is set in @flags, the link is not going to be managed by |
196 | * automatically when the consumer device driver unbinds from it. Analogously, | 196 | * the driver core and, in particular, the caller of this function is expected |
197 | * if DL_FLAG_AUTOREMOVE_SUPPLIER is set in @flags, the link will be removed | 197 | * to drop the reference to the link acquired by it directly. |
198 | * automatically when the supplier device driver unbinds from it. | 198 | * |
199 | * If that flag is not set, however, the caller of this function is handing the | ||
200 | * management of the link over to the driver core entirely and its return value | ||
201 | * can only be used to check whether or not the link is present. In that case, | ||
202 | * the DL_FLAG_AUTOREMOVE_CONSUMER and DL_FLAG_AUTOREMOVE_SUPPLIER device link | ||
203 | * flags can be used to indicate to the driver core when the link can be safely | ||
204 | * deleted. Namely, setting one of them in @flags indicates to the driver core | ||
205 | * that the link is not going to be used (by the given caller of this function) | ||
206 | * after unbinding the consumer or supplier driver, respectively, from its | ||
207 | * device, so the link can be deleted at that point. If none of them is set, | ||
208 | * the link will be maintained until one of the devices pointed to by it (either | ||
209 | * the consumer or the supplier) is unregistered. | ||
199 | * | 210 | * |
200 | * The combination of DL_FLAG_STATELESS and either DL_FLAG_AUTOREMOVE_CONSUMER | 211 | * The combination of DL_FLAG_STATELESS and either DL_FLAG_AUTOREMOVE_CONSUMER |
201 | * or DL_FLAG_AUTOREMOVE_SUPPLIER set in @flags at the same time is invalid and | 212 | * or DL_FLAG_AUTOREMOVE_SUPPLIER set in @flags at the same time is invalid and |
@@ -241,6 +252,14 @@ struct device_link *device_link_add(struct device *consumer, | |||
241 | goto out; | 252 | goto out; |
242 | } | 253 | } |
243 | 254 | ||
255 | /* | ||
256 | * DL_FLAG_AUTOREMOVE_SUPPLIER indicates that the link will be needed | ||
257 | * longer than for DL_FLAG_AUTOREMOVE_CONSUMER and setting them both | ||
258 | * together doesn't make sense, so prefer DL_FLAG_AUTOREMOVE_SUPPLIER. | ||
259 | */ | ||
260 | if (flags & DL_FLAG_AUTOREMOVE_SUPPLIER) | ||
261 | flags &= ~DL_FLAG_AUTOREMOVE_CONSUMER; | ||
262 | |||
244 | list_for_each_entry(link, &supplier->links.consumers, s_node) { | 263 | list_for_each_entry(link, &supplier->links.consumers, s_node) { |
245 | if (link->consumer != consumer) | 264 | if (link->consumer != consumer) |
246 | continue; | 265 | continue; |
@@ -254,12 +273,6 @@ struct device_link *device_link_add(struct device *consumer, | |||
254 | goto out; | 273 | goto out; |
255 | } | 274 | } |
256 | 275 | ||
257 | if (flags & DL_FLAG_AUTOREMOVE_CONSUMER) | ||
258 | link->flags |= DL_FLAG_AUTOREMOVE_CONSUMER; | ||
259 | |||
260 | if (flags & DL_FLAG_AUTOREMOVE_SUPPLIER) | ||
261 | link->flags |= DL_FLAG_AUTOREMOVE_SUPPLIER; | ||
262 | |||
263 | if (flags & DL_FLAG_PM_RUNTIME) { | 276 | if (flags & DL_FLAG_PM_RUNTIME) { |
264 | if (!(link->flags & DL_FLAG_PM_RUNTIME)) { | 277 | if (!(link->flags & DL_FLAG_PM_RUNTIME)) { |
265 | device_link_rpm_prepare(consumer, supplier); | 278 | device_link_rpm_prepare(consumer, supplier); |
@@ -269,7 +282,25 @@ struct device_link *device_link_add(struct device *consumer, | |||
269 | refcount_inc(&link->rpm_active); | 282 | refcount_inc(&link->rpm_active); |
270 | } | 283 | } |
271 | 284 | ||
272 | kref_get(&link->kref); | 285 | if (flags & DL_FLAG_STATELESS) { |
286 | kref_get(&link->kref); | ||
287 | goto out; | ||
288 | } | ||
289 | |||
290 | /* | ||
291 | * If the life time of the link following from the new flags is | ||
292 | * longer than indicated by the flags of the existing link, | ||
293 | * update the existing link to stay around longer. | ||
294 | */ | ||
295 | if (flags & DL_FLAG_AUTOREMOVE_SUPPLIER) { | ||
296 | if (link->flags & DL_FLAG_AUTOREMOVE_CONSUMER) { | ||
297 | link->flags &= ~DL_FLAG_AUTOREMOVE_CONSUMER; | ||
298 | link->flags |= DL_FLAG_AUTOREMOVE_SUPPLIER; | ||
299 | } | ||
300 | } else if (!(flags & DL_FLAG_AUTOREMOVE_CONSUMER)) { | ||
301 | link->flags &= ~(DL_FLAG_AUTOREMOVE_CONSUMER | | ||
302 | DL_FLAG_AUTOREMOVE_SUPPLIER); | ||
303 | } | ||
273 | goto out; | 304 | goto out; |
274 | } | 305 | } |
275 | 306 | ||
@@ -419,8 +450,16 @@ static void __device_link_del(struct kref *kref) | |||
419 | } | 450 | } |
420 | #endif /* !CONFIG_SRCU */ | 451 | #endif /* !CONFIG_SRCU */ |
421 | 452 | ||
453 | static void device_link_put_kref(struct device_link *link) | ||
454 | { | ||
455 | if (link->flags & DL_FLAG_STATELESS) | ||
456 | kref_put(&link->kref, __device_link_del); | ||
457 | else | ||
458 | WARN(1, "Unable to drop a managed device link reference\n"); | ||
459 | } | ||
460 | |||
422 | /** | 461 | /** |
423 | * device_link_del - Delete a link between two devices. | 462 | * device_link_del - Delete a stateless link between two devices. |
424 | * @link: Device link to delete. | 463 | * @link: Device link to delete. |
425 | * | 464 | * |
426 | * The caller must ensure proper synchronization of this function with runtime | 465 | * The caller must ensure proper synchronization of this function with runtime |
@@ -432,14 +471,14 @@ void device_link_del(struct device_link *link) | |||
432 | { | 471 | { |
433 | device_links_write_lock(); | 472 | device_links_write_lock(); |
434 | device_pm_lock(); | 473 | device_pm_lock(); |
435 | kref_put(&link->kref, __device_link_del); | 474 | device_link_put_kref(link); |
436 | device_pm_unlock(); | 475 | device_pm_unlock(); |
437 | device_links_write_unlock(); | 476 | device_links_write_unlock(); |
438 | } | 477 | } |
439 | EXPORT_SYMBOL_GPL(device_link_del); | 478 | EXPORT_SYMBOL_GPL(device_link_del); |
440 | 479 | ||
441 | /** | 480 | /** |
442 | * device_link_remove - remove a link between two devices. | 481 | * device_link_remove - Delete a stateless link between two devices. |
443 | * @consumer: Consumer end of the link. | 482 | * @consumer: Consumer end of the link. |
444 | * @supplier: Supplier end of the link. | 483 | * @supplier: Supplier end of the link. |
445 | * | 484 | * |
@@ -458,7 +497,7 @@ void device_link_remove(void *consumer, struct device *supplier) | |||
458 | 497 | ||
459 | list_for_each_entry(link, &supplier->links.consumers, s_node) { | 498 | list_for_each_entry(link, &supplier->links.consumers, s_node) { |
460 | if (link->consumer == consumer) { | 499 | if (link->consumer == consumer) { |
461 | kref_put(&link->kref, __device_link_del); | 500 | device_link_put_kref(link); |
462 | break; | 501 | break; |
463 | } | 502 | } |
464 | } | 503 | } |