offload.c 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * offload.c - USB offload related functions
  4. *
  5. * Copyright (c) 2025, Google LLC.
  6. *
  7. * Author: Guan-Yu Lin
  8. */
  9. #include <linux/usb.h>
  10. #include "usb.h"
  11. /**
  12. * usb_offload_get - increment the offload_usage of a USB device
  13. * @udev: the USB device to increment its offload_usage
  14. *
  15. * Incrementing the offload_usage of a usb_device indicates that offload is
  16. * enabled on this usb_device; that is, another entity is actively handling USB
  17. * transfers. This information allows the USB driver to adjust its power
  18. * management policy based on offload activity.
  19. *
  20. * Return: 0 on success. A negative error code otherwise.
  21. */
  22. int usb_offload_get(struct usb_device *udev)
  23. {
  24. int ret = 0;
  25. if (!usb_get_dev(udev))
  26. return -ENODEV;
  27. if (pm_runtime_get_if_active(&udev->dev) != 1) {
  28. ret = -EBUSY;
  29. goto err_rpm;
  30. }
  31. spin_lock(&udev->offload_lock);
  32. if (udev->offload_pm_locked) {
  33. ret = -EAGAIN;
  34. goto err;
  35. }
  36. udev->offload_usage++;
  37. err:
  38. spin_unlock(&udev->offload_lock);
  39. pm_runtime_put_autosuspend(&udev->dev);
  40. err_rpm:
  41. usb_put_dev(udev);
  42. return ret;
  43. }
  44. EXPORT_SYMBOL_GPL(usb_offload_get);
  45. /**
  46. * usb_offload_put - drop the offload_usage of a USB device
  47. * @udev: the USB device to drop its offload_usage
  48. *
  49. * The inverse operation of usb_offload_get, which drops the offload_usage of
  50. * a USB device. This information allows the USB driver to adjust its power
  51. * management policy based on offload activity.
  52. *
  53. * Return: 0 on success. A negative error code otherwise.
  54. */
  55. int usb_offload_put(struct usb_device *udev)
  56. {
  57. int ret = 0;
  58. if (!usb_get_dev(udev))
  59. return -ENODEV;
  60. if (pm_runtime_get_if_active(&udev->dev) != 1) {
  61. ret = -EBUSY;
  62. goto err_rpm;
  63. }
  64. spin_lock(&udev->offload_lock);
  65. if (udev->offload_pm_locked) {
  66. ret = -EAGAIN;
  67. goto err;
  68. }
  69. /* Drop the count when it wasn't 0, ignore the operation otherwise. */
  70. if (udev->offload_usage)
  71. udev->offload_usage--;
  72. err:
  73. spin_unlock(&udev->offload_lock);
  74. pm_runtime_put_autosuspend(&udev->dev);
  75. err_rpm:
  76. usb_put_dev(udev);
  77. return ret;
  78. }
  79. EXPORT_SYMBOL_GPL(usb_offload_put);
  80. /**
  81. * usb_offload_check - check offload activities on a USB device
  82. * @udev: the USB device to check its offload activity.
  83. *
  84. * Check if there are any offload activity on the USB device right now. This
  85. * information could be used for power management or other forms of resource
  86. * management.
  87. *
  88. * The caller must hold @udev's device lock. In addition, the caller should
  89. * ensure the device itself and the downstream usb devices are all marked as
  90. * "offload_pm_locked" to ensure the correctness of the return value.
  91. *
  92. * Returns true on any offload activity, false otherwise.
  93. */
  94. bool usb_offload_check(struct usb_device *udev) __must_hold(&udev->dev->mutex)
  95. {
  96. struct usb_device *child;
  97. bool active = false;
  98. int port1;
  99. if (udev->offload_usage)
  100. return true;
  101. usb_hub_for_each_child(udev, port1, child) {
  102. usb_lock_device(child);
  103. active = usb_offload_check(child);
  104. usb_unlock_device(child);
  105. if (active)
  106. break;
  107. }
  108. return active;
  109. }
  110. EXPORT_SYMBOL_GPL(usb_offload_check);
  111. /**
  112. * usb_offload_set_pm_locked - set the PM lock state of a USB device
  113. * @udev: the USB device to modify
  114. * @locked: the new lock state
  115. *
  116. * Setting @locked to true prevents offload_usage from being modified. This
  117. * ensures that offload activities cannot be started or stopped during critical
  118. * power management transitions, maintaining a stable state for the duration
  119. * of the transition.
  120. */
  121. void usb_offload_set_pm_locked(struct usb_device *udev, bool locked)
  122. {
  123. spin_lock(&udev->offload_lock);
  124. udev->offload_pm_locked = locked;
  125. spin_unlock(&udev->offload_lock);
  126. }
  127. EXPORT_SYMBOL_GPL(usb_offload_set_pm_locked);