mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 19:08:57 +09:00
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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user