usb: dwc3: gadget: fix request already in flight

Test on RV1126 board with UVC function. When do UVC streaming on/off
@H265 3840 * 2160 stress test, it fails to streaming on UVC with the
following log:

[ 1589.834573] WARNING: CPU: 3 PID: 3075 at drivers/usb/dwc3/gadget.c:1611 dwc3_gadget_ep_queue+0x148/0x178
[ 1589.834593] ep3in: request 638c13d3 already in flight
[ 1589.834603] Modules linked in: galcore(O)
[ 1589.834622] CPU: 3 PID: 3075 Comm: kworker/3:2 Tainted: G        W  O      4.19.111 #2
[ 1589.834631] Hardware name: Generic DT based system
[ 1589.834648] Workqueue: events uvcg_video_pump
[ 1589.834673] [<b010f408>] (unwind_backtrace) from [<b010b96c>] (show_stack+0x10/0x14)
[ 1589.834741] [<b010b96c>] (show_stack) from [<b0645104>] (dump_stack+0x90/0xa4)
[ 1589.834766] [<b0645104>] (dump_stack) from [<b0126204>] (__warn+0xfc/0x114)
[ 1589.834787] [<b0126204>] (__warn) from [<b0126260>] (warn_slowpath_fmt+0x44/0x68)
[ 1589.834806] [<b0126260>] (warn_slowpath_fmt) from [<b04571d8>] (dwc3_gadget_ep_queue+0x148/0x178)
[ 1589.834826] [<b04571d8>] (dwc3_gadget_ep_queue) from [<b0472074>] (uvcg_video_pump+0x94/0x164)
[ 1589.834849] [<b0472074>] (uvcg_video_pump) from [<b013cce0>] (process_one_work+0x1f0/0x408)
[ 1589.834869] [<b013cce0>] (process_one_work) from [<b013d9b8>] (worker_thread+0x30/0x564)
[ 1589.834891] [<b013d9b8>] (worker_thread) from [<b0142910>] (kthread+0x140/0x170)
[ 1589.834907] [<b0142910>] (kthread) from [<b01010d8>] (ret_from_fork+0x14/0x3c)
[ 1589.834922] Exception stack(0xbbdfffb0 to 0xbbdffff8)
[ 1589.834938] ffa0:                                     00000000 00000000 00000000 00000000
[ 1589.834953] ffc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
[ 1589.834966] ffe0: 00000000 00000000 00000000 00000000 00000013 00000000
[ 1589.834979] ---[ end trace b30d445a1f050523 ]---
[ 1589.834979] ---[ end trace b30d445a1f050523 ]---
[ 1589.834992] Failed to queue request (-22).

It's because that the __dwc3_gadget_start_isoc is called very late after
XferNotReady, so the frame number is outdated and start transfer fail
with the cmd_status "DEPEVT_TRANSFER_BUS_EXPIRY". In this case, the dwc3
driver return -EINVAL to the UVC function driver without delete and unmap
the failed request, this cause the request requeue fail next time.

Change-Id: I4cc919bcd4e1e0abbb6a929483e6fc2fe7dc9750
Signed-off-by: William Wu <william.wu@rock-chips.com>
This commit is contained in:
William Wu
2021-03-17 10:40:05 +08:00
committed by Tao Huang
parent 582ea1370a
commit a140608dec

View File

@@ -1682,6 +1682,31 @@ static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
return ret;
}
static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *req)
{
int i;
/*
* If request was already started, this means we had to
* stop the transfer. With that we also need to ignore
* all TRBs used by the request, however TRBs can only
* be modified after completion of END_TRANSFER
* command. So what we do here is that we wait for
* END_TRANSFER completion and only after that, we jump
* over TRBs by clearing HWO and incrementing dequeue
* pointer.
*/
for (i = 0; i < req->num_trbs; i++) {
struct dwc3_trb *trb;
trb = &dep->trb_pool[dep->trb_dequeue];
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
dwc3_ep_inc_deq(dep);
}
req->num_trbs = 0;
}
static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
{
struct dwc3 *dwc = dep->dwc;
@@ -1698,8 +1723,14 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
if (WARN(req->status < DWC3_REQUEST_STATUS_COMPLETED,
"%s: request %pK already in flight\n",
dep->name, &req->request))
dep->name, &req->request)) {
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
dwc3_gadget_ep_skip_trbs(dep, req);
req->status = DWC3_REQUEST_STATUS_COMPLETED;
dwc3_gadget_del_and_unmap_request(dep, req, -EINVAL);
}
return -EINVAL;
}
pm_runtime_get(dwc->dev);
@@ -1758,31 +1789,6 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
return ret;
}
static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *req)
{
int i;
/*
* If request was already started, this means we had to
* stop the transfer. With that we also need to ignore
* all TRBs used by the request, however TRBs can only
* be modified after completion of END_TRANSFER
* command. So what we do here is that we wait for
* END_TRANSFER completion and only after that, we jump
* over TRBs by clearing HWO and incrementing dequeue
* pointer.
*/
for (i = 0; i < req->num_trbs; i++) {
struct dwc3_trb *trb;
trb = &dep->trb_pool[dep->trb_dequeue];
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
dwc3_ep_inc_deq(dep);
}
req->num_trbs = 0;
}
static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep)
{
struct dwc3_request *req;