From a140608dec8aa7ccebf11707635fe09ded626d57 Mon Sep 17 00:00:00 2001 From: William Wu Date: Wed, 17 Mar 2021 10:40:05 +0800 Subject: [PATCH] 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] [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [ 1589.834741] [] (show_stack) from [] (dump_stack+0x90/0xa4) [ 1589.834766] [] (dump_stack) from [] (__warn+0xfc/0x114) [ 1589.834787] [] (__warn) from [] (warn_slowpath_fmt+0x44/0x68) [ 1589.834806] [] (warn_slowpath_fmt) from [] (dwc3_gadget_ep_queue+0x148/0x178) [ 1589.834826] [] (dwc3_gadget_ep_queue) from [] (uvcg_video_pump+0x94/0x164) [ 1589.834849] [] (uvcg_video_pump) from [] (process_one_work+0x1f0/0x408) [ 1589.834869] [] (process_one_work) from [] (worker_thread+0x30/0x564) [ 1589.834891] [] (worker_thread) from [] (kthread+0x140/0x170) [ 1589.834907] [] (kthread) from [] (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 --- drivers/usb/dwc3/gadget.c | 58 +++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 22d2fd8f753d..534a3bf4c679 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -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;