aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base/core.c')
-rw-r--r--drivers/base/core.c69
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
453static 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}
439EXPORT_SYMBOL_GPL(device_link_del); 478EXPORT_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 }