Commit 51f1954a authored by Thinh Nguyen's avatar Thinh Nguyen Committed by Greg Kroah-Hartman

usb: dwc3: gadget: Fix dwc3_calc_trbs_left()

We can't depend on the TRB's HWO bit to determine if the TRB ring is
"full". A TRB is only available when the driver had processed it, not
when the controller consumed and relinquished the TRB's ownership to the
driver. Otherwise, the driver may overwrite unprocessed TRBs. This can
happen when many transfer events accumulate and the system is slow to
process them and/or when there are too many small requests.

If a request is in the started_list, that means there is one or more
unprocessed TRBs remained. Check this instead of the TRB's HWO bit
whether the TRB ring is full.

Fixes: c4233573 ("usb: dwc3: gadget: prepare TRBs on update transfers too")
Cc: <stable@vger.kernel.org>
Acked-by: default avatarFelipe Balbi <balbi@kernel.org>
Signed-off-by: default avatarThinh Nguyen <Thinh.Nguyen@synopsys.com>
Link: https://lore.kernel.org/r/e91e975affb0d0d02770686afc3a5b9eb84409f6.1629335416.git.Thinh.Nguyen@synopsys.comSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 662b9329
...@@ -940,19 +940,19 @@ static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u8 index) ...@@ -940,19 +940,19 @@ static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u8 index)
static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep) static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
{ {
struct dwc3_trb *tmp;
u8 trbs_left; u8 trbs_left;
/* /*
* If enqueue & dequeue are equal than it is either full or empty. * If the enqueue & dequeue are equal then the TRB ring is either full
* * or empty. It's considered full when there are DWC3_TRB_NUM-1 of TRBs
* One way to know for sure is if the TRB right before us has HWO bit * pending to be processed by the driver.
* set or not. If it has, then we're definitely full and can't fit any
* more transfers in our ring.
*/ */
if (dep->trb_enqueue == dep->trb_dequeue) { if (dep->trb_enqueue == dep->trb_dequeue) {
tmp = dwc3_ep_prev_trb(dep, dep->trb_enqueue); /*
if (tmp->ctrl & DWC3_TRB_CTRL_HWO) * If there is any request remained in the started_list at
* this point, that means there is no TRB available.
*/
if (!list_empty(&dep->started_list))
return 0; return 0;
return DWC3_TRB_NUM - 1; return DWC3_TRB_NUM - 1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment