Signed-off-by: Shixiong Ou <[email protected]>
---
drivers/gpu/drm/udl/udl_drv.h | 14 +++++++++++++-
drivers/gpu/drm/udl/udl_main.c | 30 +++++++++++++++++++++++++-----
drivers/gpu/drm/udl/udl_modeset.c | 6 +++---
drivers/gpu/drm/udl/udl_transfer.c | 2 +-
4 files changed, 42 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h
index 145bb95ccc48..43cff97e12f7 100644
--- a/drivers/gpu/drm/udl/udl_drv.h
+++ b/drivers/gpu/drm/udl/udl_drv.h
@@ -11,6 +11,7 @@
#ifndef UDL_DRV_H
#define UDL_DRV_H
+#include <linux/jiffies.h>
#include <linux/mm_types.h>
#include <linux/usb.h>
@@ -46,6 +47,7 @@ struct urb_list {
int available;
int count;
size_t size;
+ bool wedged;
};
struct udl_device {
@@ -72,7 +74,17 @@ static inline struct usb_device *udl_to_usb_device(struct
udl_device *udl)
int udl_modeset_init(struct udl_device *udl);
struct drm_connector *udl_connector_init(struct drm_device *dev);
-struct urb *udl_get_urb(struct udl_device *udl);
+/*
+ * How long to wait for a free URB before giving up. The damage path runs
+ * on every atomic update, so keep its timeout short: a stalled device never
+ * returns its URBs and the wait would otherwise stall the whole display
+ * stack on every operation. Modeset is low-frequency and worth waiting
+ * longer for. Once the pool is flagged wedged, neither path blocks at all.
+ */
+#define UDL_GET_URB_TIMEOUT_DAMAGE msecs_to_jiffies(100)
+#define UDL_GET_URB_TIMEOUT_MODESET msecs_to_jiffies(2000)
+
+struct urb *udl_get_urb(struct udl_device *udl, long timeout);
int udl_submit_urb(struct udl_device *udl, struct urb *urb, size_t len);
void udl_sync_pending_urbs(struct udl_device *udl);
diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c
index 17950fe3a0ec..b92cd5300030 100644
--- a/drivers/gpu/drm/udl/udl_main.c
+++ b/drivers/gpu/drm/udl/udl_main.c
@@ -169,6 +169,7 @@ void udl_urb_completion(struct urb *urb)
spin_lock_irqsave(&udl->urbs.lock, flags);
list_add_tail(&unode->entry, &udl->urbs.list);
udl->urbs.available++;
+ udl->urbs.wedged = false;
spin_unlock_irqrestore(&udl->urbs.lock, flags);
wake_up(&udl->urbs.sleep);
@@ -213,6 +214,7 @@ static int udl_alloc_urb_list(struct udl_device *udl, int
count, size_t size)
init_waitqueue_head(&udl->urbs.sleep);
udl->urbs.count = 0;
udl->urbs.available = 0;
+ udl->urbs.wedged = false;
retry:
udl->urbs.size = size;
@@ -265,16 +267,34 @@ static struct urb *udl_get_urb_locked(struct udl_device
*udl, long timeout)
assert_spin_locked(&udl->urbs.lock);
+ /* A device that stopped completing transfers will never wake us up:
+ * fail fast instead of stalling callers for the full timeout on every
+ * call. Allocations for teardown (MAX_SCHEDULE_TIMEOUT) still wait,
+ * since the USB core returns all URBs on disconnect.
+ */
+ if (udl->urbs.wedged && timeout != MAX_SCHEDULE_TIMEOUT) {
+ if (list_empty(&udl->urbs.list))
+ return NULL;
+ goto take;
+ }
+
/* Wait for an in-flight buffer to complete and get re-queued */
if (!wait_event_lock_irq_timeout(udl->urbs.sleep,
!udl->urbs.count ||
!list_empty(&udl->urbs.list),
udl->urbs.lock, timeout)) {
- DRM_INFO("wait for urb interrupted: available: %d\n",
- udl->urbs.available);
+ if (timeout == MAX_SCHEDULE_TIMEOUT) {
+ DRM_INFO("wait for urb interrupted: available: %d\n",
+ udl->urbs.available);
+ } else {
+ udl->urbs.wedged = true;
+ drm_warn_ratelimited(&udl->drm,
+ "no URB returned within %u ms (available %d); device
stalled, dropping frames\n",
+ jiffies_to_msecs(timeout), udl->urbs.available);
+ }
return NULL;
}
-
+take:
if (!udl->urbs.count)
return NULL;
@@ -285,12 +305,12 @@ static struct urb *udl_get_urb_locked(struct udl_device *udl, long timeout)
return unode->urb;
}
-struct urb *udl_get_urb(struct udl_device *udl)
+struct urb *udl_get_urb(struct udl_device *udl, long timeout)
{
struct urb *urb;
spin_lock_irq(&udl->urbs.lock);
- urb = udl_get_urb_locked(udl, HZ * 2);
+ urb = udl_get_urb_locked(udl, timeout);
spin_unlock_irq(&udl->urbs.lock);
return urb;
}
diff --git a/drivers/gpu/drm/udl/udl_modeset.c
b/drivers/gpu/drm/udl/udl_modeset.c
index 289711035b67..c907846048f8 100644
--- a/drivers/gpu/drm/udl/udl_modeset.c
+++ b/drivers/gpu/drm/udl/udl_modeset.c
@@ -218,7 +218,7 @@ static int udl_handle_damage(struct drm_framebuffer *fb,
return ret;
log_bpp = ret;
- urb = udl_get_urb(udl);
+ urb = udl_get_urb(udl, UDL_GET_URB_TIMEOUT_DAMAGE);
if (!urb)
return -ENOMEM;
cmd = urb->transfer_buffer;
@@ -342,7 +342,7 @@ static void udl_crtc_helper_atomic_enable(struct drm_crtc
*crtc, struct drm_atom
if (!drm_dev_enter(dev, &idx))
return;
- urb = udl_get_urb(udl);
+ urb = udl_get_urb(udl, UDL_GET_URB_TIMEOUT_MODESET);
if (!urb) {
drm_err_ratelimited(dev, "get urb failed when enabling crtc\n");
goto out;
@@ -377,7 +377,7 @@ static void udl_crtc_helper_atomic_disable(struct drm_crtc
*crtc, struct drm_ato
if (!drm_dev_enter(dev, &idx))
return;
- urb = udl_get_urb(udl);
+ urb = udl_get_urb(udl, UDL_GET_URB_TIMEOUT_MODESET);
if (!urb)
goto out;
diff --git a/drivers/gpu/drm/udl/udl_transfer.c b/drivers/gpu/drm/udl/udl_transfer.c
index 7d670b3a5293..bfff17bba58d 100644
--- a/drivers/gpu/drm/udl/udl_transfer.c
+++ b/drivers/gpu/drm/udl/udl_transfer.c
@@ -202,7 +202,7 @@ int udl_render_hline(struct udl_device *udl, int log_bpp,
struct urb **urb_ptr,
int ret = udl_submit_urb(udl, urb, len);
if (ret)
return ret;
- urb = udl_get_urb(udl);
+ urb = udl_get_urb(udl, UDL_GET_URB_TIMEOUT_DAMAGE);
if (!urb)
return -EAGAIN;
*urb_ptr = urb;