[PATCH V6 02/10] usb: dwc3: gadget: Add stream timeout handler for avoiding deadlock

From: Anurag Kumar Vulisha
Date: Sat Oct 13 2018 - 09:15:47 EST


According to dwc3 databook when streams are enabled, it may be possible
for the host and gadget to become out of sync, where gadget may wait for
host to issue prime transcation and host may wait for gadget to issue
ERDY. To avoid such potential deadlock conditions, a timer is implemented
in udc/core.c and which is started after queuing a valid request in
usb_ep_queue(). This timer would be stopped by the gadget driver when
a valid stream event is found, otherwise the timer gets expired after
STREAM_TIMEOUT_MS value and stream_timeout_function() which is registered
as a callback function to usb_ep->ops->stream_timeout is called by udc
core.

As a part of recovery mechanism, the stream_timeout_function() stops
the active transfer on the endpoint and starts the transfer again on
the endpoint. Doing so, will reset the stream into ready state and ERDY
is sent to the host, thus the deadlock is avoided.

Signed-off-by: Anurag Kumar Vulisha <anurag.kumar.vulisha@xxxxxxxxxx>
---
Changes in v6:
1. This patch is newly added in this series
---
drivers/usb/dwc3/gadget.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)

diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 032ea7d..aab2970 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -573,6 +573,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE
| DWC3_DEPCFG_STREAM_EVENT_EN;
dep->stream_capable = true;
+ dep->endpoint.stream_capable = true;
}

if (!usb_endpoint_xfer_control(desc))
@@ -1535,6 +1536,18 @@ static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep)
return ret;
}

+static void stream_timeout_function(struct usb_ep *ep)
+{
+ struct dwc3_ep *dep = to_dwc3_ep(ep);
+ struct dwc3 *dwc = dep->dwc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_stop_active_transfer(dep, true);
+ __dwc3_gadget_kick_transfer(dep);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+}
+
/* -------------------------------------------------------------------------- */

static struct usb_endpoint_descriptor dwc3_gadget_ep0_desc = {
@@ -1563,6 +1576,7 @@ static const struct usb_ep_ops dwc3_gadget_ep_ops = {
.dequeue = dwc3_gadget_ep_dequeue,
.set_halt = dwc3_gadget_ep_set_halt,
.set_wedge = dwc3_gadget_ep_set_wedge,
+ .stream_timeout = stream_timeout_function,
};

/* -------------------------------------------------------------------------- */
@@ -2469,6 +2483,10 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
}
break;
case DWC3_DEPEVT_STREAMEVT:
+ if ((event->status == DEPEVT_STREAMEVT_FOUND) &&
+ timer_pending(&dep->endpoint.stream_timeout_timer))
+ del_timer(&dep->endpoint.stream_timeout_timer);
+
case DWC3_DEPEVT_XFERCOMPLETE:
case DWC3_DEPEVT_RXTXFIFOEVT:
break;
--
2.1.1