It's helpful to be able to look at device link details from sysfs. So, expose it in sysfs. Say device-A is supplier of device-B. These are the additional files this patch would create: /sys/class/devlink/device-A:device-B/ auto_remove_on consumer/ -> .../device-B/ runtime_pm status supplier/ -> .../device-A/ sync_state_only /sys/devices/.../device-A/ consumer:device-B/ -> /sys/class/devlink/device-A:device-B/ /sys/devices/.../device-B/ supplier:device-A/ -> /sys/class/devlink/device-A:device-B/ That way: To get a list of all the device link in the system: ls /sys/class/devlink/ To get the consumer names and links of a device: ls -d /sys/devices/.../device-X/consumer:* To get the supplier names and links of a device: ls -d /sys/devices/.../device-X/supplier:*
Unified · Split
| @@ -0,0 +1,126 @@ | ||
| 1 | +What: /sys/class/devlink/.../ | |
| 2 | +Date: May 2020 | |
| 3 | +Contact: Saravana Kannan <saravanak@google.com> | |
| 4 | +Description: | |
| 5 | + Provide a place in sysfs for the device link objects in the | |
| 6 | + kernel at any given time. The name of a device link directory, | |
| 7 | + denoted as ... above, is of the form <supplier>:<consumer> | |
| 8 | + where <supplier> is the supplier device name and <consumer> is | |
| 9 | + the consumer device name. | |
| 10 | + | |
| 11 | +What: /sys/class/devlink/.../auto_remove_on | |
| 12 | +Date: May 2020 | |
| 13 | +Contact: Saravana Kannan <saravanak@google.com> | |
| 14 | +Description: | |
| 15 | + This file indicates if the device link will ever be | |
| 16 | + automatically removed by the driver core when the consumer and | |
| 17 | + supplier devices themselves are still present. | |
| 18 | + | |
| 19 | + This will be one of the following strings: | |
| 20 | + | |
| 21 | + 'consumer unbind' | |
| 22 | + 'supplier unbind' | |
| 23 | + 'never' | |
| 24 | + | |
| 25 | + 'consumer unbind' means the device link will be removed when | |
| 26 | + the consumer's driver is unbound from the consumer device. | |
| 27 | + | |
| 28 | + 'supplier unbind' means the device link will be removed when | |
| 29 | + the supplier's driver is unbound from the supplier device. | |
| 30 | + | |
| 31 | + 'never' means the device link will not be automatically removed | |
| 32 | + when as long as the supplier and consumer devices themselves | |
| 33 | + are still present. | |
| 34 | + | |
| 35 | +What: /sys/class/devlink/.../consumer | |
| 36 | +Date: May 2020 | |
| 37 | +Contact: Saravana Kannan <saravanak@google.com> | |
| 38 | +Description: | |
| 39 | + This file is a symlink to the consumer device's sysfs directory. | |
| 40 | + | |
| 41 | +What: /sys/class/devlink/.../runtime_pm | |
| 42 | +Date: May 2020 | |
| 43 | +Contact: Saravana Kannan <saravanak@google.com> | |
| 44 | +Description: | |
| 45 | + This file indicates if the device link has any impact on the | |
| 46 | + runtime power management behavior of the consumer and supplier | |
| 47 | + devices. For example: Making sure the supplier doesn't enter | |
| 48 | + runtime suspend while the consumer is active. | |
| 49 | + | |
| 50 | + This will be one of the following strings: | |
| 51 | + | |
| 52 | + '0' - Does not affect runtime power management | |
| 53 | + '1' - Affects runtime power management | |
| 54 | + | |
| 55 | +What: /sys/class/devlink/.../status | |
| 56 | +Date: May 2020 | |
| 57 | +Contact: Saravana Kannan <saravanak@google.com> | |
| 58 | +Description: | |
| 59 | + This file indicates the status of the device link. The status | |
| 60 | + of a device link is affected by whether the supplier and | |
| 61 | + consumer devices have been bound to their corresponding | |
| 62 | + drivers. The status of a device link also affects the binding | |
| 63 | + and unbinding of the supplier and consumer devices with their | |
| 64 | + drivers and also affects whether the software state of the | |
| 65 | + supplier device is synced with the hardware state of the | |
| 66 | + supplier device after boot up. | |
| 67 | + See also: sysfs-devices-state_synced. | |
| 68 | + | |
| 69 | + This will be one of the following strings: | |
| 70 | + | |
| 71 | + 'not tracked' | |
| 72 | + 'dormant' | |
| 73 | + 'available' | |
| 74 | + 'consumer probing' | |
| 75 | + 'active' | |
| 76 | + 'supplier unbinding' | |
| 77 | + 'unknown' | |
| 78 | + | |
| 79 | + 'not tracked' means this device link does not track the status | |
| 80 | + and has no impact on the binding, unbinding and syncing the | |
| 81 | + hardware and software device state. | |
| 82 | + | |
| 83 | + 'dormant' means the supplier and the consumer devices have not | |
| 84 | + bound to their driver. | |
| 85 | + | |
| 86 | + 'available' means the supplier has bound to its driver and is | |
| 87 | + available to supply resources to the consumer device. | |
| 88 | + | |
| 89 | + 'consumer probing' means the consumer device is currently | |
| 90 | + trying to bind to its driver. | |
| 91 | + | |
| 92 | + 'active' means the supplier and consumer devices have both | |
| 93 | + bound successfully to their drivers. | |
| 94 | + | |
| 95 | + 'supplier unbinding' means the supplier devices is currently in | |
| 96 | + the process of unbinding from its driver. | |
| 97 | + | |
| 98 | + 'unknown' means the state of the device link is not any of the | |
| 99 | + above. If this is ever the value, there's a bug in the kernel. | |
| 100 | + | |
| 101 | +What: /sys/class/devlink/.../supplier | |
| 102 | +Date: May 2020 | |
| 103 | +Contact: Saravana Kannan <saravanak@google.com> | |
| 104 | +Description: | |
| 105 | + This file is a symlink to the supplier device's sysfs directory. | |
| 106 | + | |
| 107 | +What: /sys/class/devlink/.../sync_state_only | |
| 108 | +Date: May 2020 | |
| 109 | +Contact: Saravana Kannan <saravanak@google.com> | |
| 110 | +Description: | |
| 111 | + This file indicates if the device link is limited to only | |
| 112 | + affecting the syncing of the hardware and software state of the | |
| 113 | + supplier device. | |
| 114 | + | |
| 115 | + This will be one of the following strings: | |
| 116 | + | |
| 117 | + '0' | |
| 118 | + '1' - Affects runtime power management | |
| 119 | + | |
| 120 | + '0' means the device link can affect other device behaviors | |
| 121 | + like binding/unbinding, suspend/resume, runtime power | |
| 122 | + management, etc. | |
| 123 | + | |
| 124 | + '1' means the device link will only affect the syncing of | |
| 125 | + hardware and software state of the supplier device after boot | |
| 126 | + up and doesn't not affect other behaviors of the devices. | |
| @@ -0,0 +1,8 @@ | ||
| 1 | +What: /sys/devices/.../consumer:<consumer> | |
| 2 | +Date: May 2020 | |
| 3 | +Contact: Saravana Kannan <saravanak@google.com> | |
| 4 | +Description: | |
| 5 | + The /sys/devices/.../consumer:<consumer> are symlinks to device | |
| 6 | + links where this device is the supplier. <consumer> denotes the | |
| 7 | + name of the consumer in that device link. There can be zero or | |
| 8 | + more of these symlinks for a given device. | |
| @@ -0,0 +1,8 @@ | ||
| 1 | +What: /sys/devices/.../supplier:<supplier> | |
| 2 | +Date: May 2020 | |
| 3 | +Contact: Saravana Kannan <saravanak@google.com> | |
| 4 | +Description: | |
| 5 | + The /sys/devices/.../supplier:<supplier> are symlinks to device | |
| 6 | + links where this device is the consumer. <supplier> denotes the | |
| 7 | + name of the supplier in that device link. There can be zero or | |
| 8 | + more of these symlinks for a given device. | |
| @@ -235,6 +235,186 @@ void device_pm_move_to_tail(struct device *dev) · 234 unmodified lines | ||
| 235 | 235 | device_links_read_unlock(idx); |
| 236 | 236 | } |
| 237 | 237 | |
| 238 | +#define to_devlink(dev) container_of((dev), struct device_link, link_dev) | |
| 239 | + | |
| 240 | +static ssize_t status_show(struct device *dev, | |
| 241 | + struct device_attribute *attr, char *buf) | |
| 242 | +{ | |
| 243 | + char *status; | |
| 244 | + | |
| 245 | + switch (to_devlink(dev)->status) { | |
| 246 | + case DL_STATE_NONE: | |
| 247 | + status = "not tracked"; break; | |
| 248 | + case DL_STATE_DORMANT: | |
| 249 | + status = "dormant"; break; | |
| 250 | + case DL_STATE_AVAILABLE: | |
| 251 | + status = "available"; break; | |
| 252 | + case DL_STATE_CONSUMER_PROBE: | |
| 253 | + status = "consumer probing"; break; | |
| 254 | + case DL_STATE_ACTIVE: | |
| 255 | + status = "active"; break; | |
| 256 | + case DL_STATE_SUPPLIER_UNBIND: | |
| 257 | + status = "supplier unbinding"; break; | |
| 258 | + default: | |
| 259 | + status = "unknown"; break; | |
| 260 | + } | |
| 261 | + return sprintf(buf, "%s\n", status); | |
| 262 | +} | |
| 263 | +static DEVICE_ATTR_RO(status); | |
| 264 | + | |
| 265 | +static ssize_t auto_remove_on_show(struct device *dev, | |
| 266 | + struct device_attribute *attr, char *buf) | |
| 267 | +{ | |
| 268 | + struct device_link *link = to_devlink(dev); | |
| 269 | + char *str; | |
| 270 | + | |
| 271 | + if (link->flags & DL_FLAG_AUTOREMOVE_SUPPLIER) | |
| 272 | + str = "supplier unbind"; | |
| 273 | + else if (link->flags & DL_FLAG_AUTOREMOVE_CONSUMER) | |
| 274 | + str = "consumer unbind"; | |
| 275 | + else | |
| 276 | + str = "never"; | |
| 277 | + | |
| 278 | + return sprintf(buf, "%s\n", str); | |
| 279 | +} | |
| 280 | +static DEVICE_ATTR_RO(auto_remove_on); | |
| 281 | + | |
| 282 | +static ssize_t runtime_pm_show(struct device *dev, | |
| 283 | + struct device_attribute *attr, char *buf) | |
| 284 | +{ | |
| 285 | + struct device_link *link = to_devlink(dev); | |
| 286 | + | |
| 287 | + return sprintf(buf, "%d\n", !!(link->flags & DL_FLAG_PM_RUNTIME)); | |
| 288 | +} | |
| 289 | +static DEVICE_ATTR_RO(runtime_pm); | |
| 290 | + | |
| 291 | +static ssize_t sync_state_only_show(struct device *dev, | |
| 292 | + struct device_attribute *attr, char *buf) | |
| 293 | +{ | |
| 294 | + struct device_link *link = to_devlink(dev); | |
| 295 | + | |
| 296 | + return sprintf(buf, "%d\n", !!(link->flags & DL_FLAG_SYNC_STATE_ONLY)); | |
| 297 | +} | |
| 298 | +static DEVICE_ATTR_RO(sync_state_only); | |
| 299 | + | |
| 300 | +static struct attribute *devlink_attrs[] = { | |
| 301 | + &dev_attr_status.attr, | |
| 302 | + &dev_attr_auto_remove_on.attr, | |
| 303 | + &dev_attr_runtime_pm.attr, | |
| 304 | + &dev_attr_sync_state_only.attr, | |
| 305 | + NULL, | |
| 306 | +}; | |
| 307 | +ATTRIBUTE_GROUPS(devlink); | |
| 308 | + | |
| 309 | +static void devlink_dev_release(struct device *dev) | |
| 310 | +{ | |
| 311 | + kfree(to_devlink(dev)); | |
| 312 | +} | |
| 313 | + | |
| 314 | +static struct class devlink_class = { | |
| 315 | + .name = "devlink", | |
| 316 | + .owner = THIS_MODULE, | |
| 317 | + .dev_groups = devlink_groups, | |
| 318 | + .dev_release = devlink_dev_release, | |
| 319 | +}; | |
| 320 | + | |
| 321 | +static int devlink_add_symlinks(struct device *dev, | |
| 322 | + struct class_interface *class_intf) | |
| 323 | +{ | |
| 324 | + int ret; | |
| 325 | + size_t len; | |
| 326 | + struct device_link *link = to_devlink(dev); | |
| 327 | + struct device *sup = link->supplier; | |
| 328 | + struct device *con = link->consumer; | |
| 329 | + char *buf; | |
| 330 | + | |
| 331 | + len = max(strlen(dev_name(sup)), strlen(dev_name(con))); | |
| 332 | + len += strlen("supplier:") + 1; | |
| 333 | + buf = kzalloc(len, GFP_KERNEL); | |
| 334 | + if (!buf) | |
| 335 | + return -ENOMEM; | |
| 336 | + | |
| 337 | + ret = sysfs_create_link(&link->link_dev.kobj, &sup->kobj, "supplier"); | |
| 338 | + if (ret) | |
| 339 | + goto out; | |
| 340 | + | |
| 341 | + ret = sysfs_create_link(&link->link_dev.kobj, &con->kobj, "consumer"); | |
| 342 | + if (ret) | |
| 343 | + goto err_con; | |
| 344 | + | |
| 345 | + snprintf(buf, len, "consumer:%s", dev_name(con)); | |
| 346 | + ret = sysfs_create_link(&sup->kobj, &link->link_dev.kobj, buf); | |
| 347 | + if (ret) | |
| 348 | + goto err_con_dev; | |
| 349 | + | |
| 350 | + snprintf(buf, len, "supplier:%s", dev_name(sup)); | |
| 351 | + ret = sysfs_create_link(&con->kobj, &link->link_dev.kobj, buf); | |
| 352 | + if (ret) | |
| 353 | + goto err_sup_dev; | |
| 354 | + | |
| 355 | + goto out; | |
| 356 | + | |
| 357 | +err_sup_dev: | |
| 358 | + snprintf(buf, len, "consumer:%s", dev_name(con)); | |
| 359 | + sysfs_remove_link(&sup->kobj, buf); | |
| 360 | +err_con_dev: | |
| 361 | + sysfs_remove_link(&link->link_dev.kobj, "consumer"); | |
| 362 | +err_con: | |
| 363 | + sysfs_remove_link(&link->link_dev.kobj, "supplier"); | |
| 364 | +out: | |
| 365 | + kfree(buf); | |
| 366 | + return ret; | |
| 367 | +} | |
| 368 | + | |
| 369 | +static void devlink_remove_symlinks(struct device *dev, | |
| 370 | + struct class_interface *class_intf) | |
| 371 | +{ | |
| 372 | + struct device_link *link = to_devlink(dev); | |
| 373 | + size_t len; | |
| 374 | + struct device *sup = link->supplier; | |
| 375 | + struct device *con = link->consumer; | |
| 376 | + char *buf; | |
| 377 | + | |
| 378 | + sysfs_remove_link(&link->link_dev.kobj, "consumer"); | |
| 379 | + sysfs_remove_link(&link->link_dev.kobj, "supplier"); | |
| 380 | + | |
| 381 | + len = max(strlen(dev_name(sup)), strlen(dev_name(con))); | |
| 382 | + len += strlen("supplier:") + 1; | |
| 383 | + buf = kzalloc(len, GFP_KERNEL); | |
| 384 | + if (!buf) { | |
| 385 | + WARN(1, "Unable to properly free device link symlinks!\n"); | |
| 386 | + return; | |
| 387 | + } | |
| 388 | + | |
| 389 | + snprintf(buf, len, "supplier:%s", dev_name(sup)); | |
| 390 | + sysfs_remove_link(&con->kobj, buf); | |
| 391 | + snprintf(buf, len, "consumer:%s", dev_name(con)); | |
| 392 | + sysfs_remove_link(&sup->kobj, buf); | |
| 393 | + kfree(buf); | |
| 394 | +} | |
| 395 | + | |
| 396 | +static struct class_interface devlink_class_intf = { | |
| 397 | + .class = &devlink_class, | |
| 398 | + .add_dev = devlink_add_symlinks, | |
| 399 | + .remove_dev = devlink_remove_symlinks, | |
| 400 | +}; | |
| 401 | + | |
| 402 | +static int __init devlink_class_init(void) | |
| 403 | +{ | |
| 404 | + int ret; | |
| 405 | + | |
| 406 | + ret = class_register(&devlink_class); | |
| 407 | + if (ret) | |
| 408 | + return ret; | |
| 409 | + | |
| 410 | + ret = class_interface_register(&devlink_class_intf); | |
| 411 | + if (ret) | |
| 412 | + class_unregister(&devlink_class); | |
| 413 | + | |
| 414 | + return ret; | |
| 415 | +} | |
| 416 | +postcore_initcall(devlink_class_init); | |
| 417 | + | |
| 238 | 418 | #define DL_MANAGED_LINK_FLAGS (DL_FLAG_AUTOREMOVE_CONSUMER | \ |
| 239 | 419 | DL_FLAG_AUTOREMOVE_SUPPLIER | \ |
| 240 | 420 | DL_FLAG_AUTOPROBE_CONSUMER | \ |
| @@ -407,13 +587,6 @@ struct device_link *device_link_add(struct device *consumer, · 166 unmodified lines | ||
| 407 | 587 | |
| 408 | 588 | refcount_set(&link->rpm_active, 1); |
| 409 | 589 | |
| 410 | − if (flags & DL_FLAG_PM_RUNTIME) { | |
| 411 | − if (flags & DL_FLAG_RPM_ACTIVE) | |
| 412 | − refcount_inc(&link->rpm_active); | |
| 413 | − | |
| 414 | − pm_runtime_new_link(consumer); | |
| 415 | − } | |
| 416 | − | |
| 417 | 590 | get_device(supplier); |
| 418 | 591 | link->supplier = supplier; |
| 419 | 592 | INIT_LIST_HEAD(&link->s_node); |
| @@ -423,6 +596,25 @@ struct device_link *device_link_add(struct device *consumer, · 3 unmodified lines | ||
| 423 | 596 | link->flags = flags; |
| 424 | 597 | kref_init(&link->kref); |
| 425 | 598 | |
| 599 | + link->link_dev.class = &devlink_class; | |
| 600 | + device_set_pm_not_required(&link->link_dev); | |
| 601 | + dev_set_name(&link->link_dev, "%s:%s", | |
| 602 | + dev_name(supplier), dev_name(consumer)); | |
| 603 | + if (device_register(&link->link_dev)) { | |
| 604 | + put_device(consumer); | |
| 605 | + put_device(supplier); | |
| 606 | + kfree(link); | |
| 607 | + link = NULL; | |
| 608 | + goto out; | |
| 609 | + } | |
| 610 | + | |
| 611 | + if (flags & DL_FLAG_PM_RUNTIME) { | |
| 612 | + if (flags & DL_FLAG_RPM_ACTIVE) | |
| 613 | + refcount_inc(&link->rpm_active); | |
| 614 | + | |
| 615 | + pm_runtime_new_link(consumer); | |
| 616 | + } | |
| 617 | + | |
| 426 | 618 | /* Determine the initial link state. */ |
| 427 | 619 | if (flags & DL_FLAG_STATELESS) |
| 428 | 620 | link->status = DL_STATE_NONE; |
| @@ -545,7 +737,7 @@ static void device_link_free(struct device_link *link) · 116 unmodified lines | ||
| 545 | 737 | |
| 546 | 738 | put_device(link->consumer); |
| 547 | 739 | put_device(link->supplier); |
| 548 | − kfree(link); | |
| 740 | + device_unregister(&link->link_dev); | |
| 549 | 741 | } |
| 550 | 742 | |
| 551 | 743 | #ifdef CONFIG_SRCU |
| @@ -1159,6 +1351,9 @@ static void device_links_purge(struct device *dev) · 607 unmodified lines | ||
| 1159 | 1351 | { |
| 1160 | 1352 | struct device_link *link, *ln; |
| 1161 | 1353 | |
| 1354 | + if (dev->class == &devlink_class) | |
| 1355 | + return; | |
| 1356 | + | |
| 1162 | 1357 | mutex_lock(&wfs_lock); |
| 1163 | 1358 | list_del(&dev->links.needs_suppliers); |
| 1164 | 1359 | mutex_unlock(&wfs_lock); |
| @@ -386,34 +386,6 @@ enum device_link_state { · 385 unmodified lines | ||
| 386 | 386 | #define DL_FLAG_MANAGED BIT(6) |
| 387 | 387 | #define DL_FLAG_SYNC_STATE_ONLY BIT(7) |
| 388 | 388 | |
| 389 | −/** | |
| 390 | − * struct device_link - Device link representation. | |
| 391 | − * @supplier: The device on the supplier end of the link. | |
| 392 | − * @s_node: Hook to the supplier device's list of links to consumers. | |
| 393 | − * @consumer: The device on the consumer end of the link. | |
| 394 | − * @c_node: Hook to the consumer device's list of links to suppliers. | |
| 395 | − * @status: The state of the link (with respect to the presence of drivers). | |
| 396 | − * @flags: Link flags. | |
| 397 | − * @rpm_active: Whether or not the consumer device is runtime-PM-active. | |
| 398 | − * @kref: Count repeated addition of the same link. | |
| 399 | − * @rcu_head: An RCU head to use for deferred execution of SRCU callbacks. | |
| 400 | − * @supplier_preactivated: Supplier has been made active before consumer probe. | |
| 401 | − */ | |
| 402 | −struct device_link { | |
| 403 | − struct device *supplier; | |
| 404 | − struct list_head s_node; | |
| 405 | − struct device *consumer; | |
| 406 | − struct list_head c_node; | |
| 407 | − enum device_link_state status; | |
| 408 | − u32 flags; | |
| 409 | − refcount_t rpm_active; | |
| 410 | − struct kref kref; | |
| 411 | −#ifdef CONFIG_SRCU | |
| 412 | − struct rcu_head rcu_head; | |
| 413 | −#endif | |
| 414 | − bool supplier_preactivated; /* Owned by consumer probe. */ | |
| 415 | −}; | |
| 416 | − | |
| 417 | 389 | /** |
| 418 | 390 | * enum dl_dev_state - Device driver presence tracking information. |
| 419 | 391 | * @DL_DEV_NO_DRIVER: There is no driver attached to the device. |
| @@ -624,6 +596,36 @@ struct device { · 204 unmodified lines | ||
| 624 | 596 | #endif |
| 625 | 597 | }; |
| 626 | 598 | |
| 599 | +/** | |
| 600 | + * struct device_link - Device link representation. | |
| 601 | + * @supplier: The device on the supplier end of the link. | |
| 602 | + * @s_node: Hook to the supplier device's list of links to consumers. | |
| 603 | + * @consumer: The device on the consumer end of the link. | |
| 604 | + * @c_node: Hook to the consumer device's list of links to suppliers. | |
| 605 | + * @link_dev: device used to expose link details in sysfs | |
| 606 | + * @status: The state of the link (with respect to the presence of drivers). | |
| 607 | + * @flags: Link flags. | |
| 608 | + * @rpm_active: Whether or not the consumer device is runtime-PM-active. | |
| 609 | + * @kref: Count repeated addition of the same link. | |
| 610 | + * @rcu_head: An RCU head to use for deferred execution of SRCU callbacks. | |
| 611 | + * @supplier_preactivated: Supplier has been made active before consumer probe. | |
| 612 | + */ | |
| 613 | +struct device_link { | |
| 614 | + struct device *supplier; | |
| 615 | + struct list_head s_node; | |
| 616 | + struct device *consumer; | |
| 617 | + struct list_head c_node; | |
| 618 | + struct device link_dev; | |
| 619 | + enum device_link_state status; | |
| 620 | + u32 flags; | |
| 621 | + refcount_t rpm_active; | |
| 622 | + struct kref kref; | |
| 623 | +#ifdef CONFIG_SRCU | |
| 624 | + struct rcu_head rcu_head; | |
| 625 | +#endif | |
| 626 | + bool supplier_preactivated; /* Owned by consumer probe. */ | |
| 627 | +}; | |
| 628 | + | |
| 627 | 629 | static inline struct device *kobj_to_dev(struct kobject *kobj) |
| 628 | 630 | { |
| 629 | 631 | return container_of(kobj, struct device, kobj); |