| From 387c2b6ba197c6df28e75359f7d892f7c8dec204 Mon Sep 17 00:00:00 2001 |
| From: Frank Li <Frank.Li@nxp.com> |
| Date: Tue, 30 Nov 2021 09:42:39 -0600 |
| Subject: usb: cdns3: gadget: fix new urb never complete if ep cancel previous requests |
| |
| From: Frank Li <Frank.Li@nxp.com> |
| |
| commit 387c2b6ba197c6df28e75359f7d892f7c8dec204 upstream. |
| |
| This issue was found at android12 MTP. |
| 1. MTP submit many out urb request. |
| 2. Cancel left requests (>20) when enough data get from host |
| 3. Send ACK by IN endpoint. |
| 4. MTP submit new out urb request. |
| 5. 4's urb never complete. |
| |
| TRACE LOG: |
| |
| MtpServer-2157 [000] d..3 1287.150391: cdns3_ep_dequeue: ep1out: req: 00000000299e6836, req buff 000000009df42287, length: 0/16384 zsi, status: -115, trb: [start:87, end:87: virt addr 0x80004000ffd50420], flags:1 SID: 0 |
| MtpServer-2157 [000] d..3 1287.150410: cdns3_gadget_giveback: ep1out: req: 00000000299e6836, req buff 000000009df42287, length: 0/16384 zsi, status: -104, trb: [start:87, end:87: virt addr 0x80004000ffd50420], flags:0 SID: 0 |
| MtpServer-2157 [000] d..3 1287.150433: cdns3_ep_dequeue: ep1out: req: 0000000080b7bde6, req buff 000000009ed5c556, length: 0/16384 zsi, status: -115, trb: [start:88, end:88: virt addr 0x80004000ffd5042c], flags:1 SID: 0 |
| MtpServer-2157 [000] d..3 1287.150446: cdns3_gadget_giveback: ep1out: req: 0000000080b7bde6, req buff 000000009ed5c556, length: 0/16384 zsi, status: -104, trb: [start:88, end:88: virt addr 0x80004000ffd5042c], flags:0 SID: 0 |
| .... |
| MtpServer-2157 [000] d..1 1293.630410: cdns3_alloc_request: ep1out: req: 00000000afbccb7d, req buff 0000000000000000, length: 0/0 zsi, status: 0, trb: [start:0, end:0: virt addr (null)], flags:0 SID: 0 |
| MtpServer-2157 [000] d..2 1293.630421: cdns3_ep_queue: ep1out: req: 00000000afbccb7d, req buff 00000000871caf90, length: 0/512 zsi, status: -115, trb: [start:0, end:0: virt addr (null)], flags:0 SID: 0 |
| MtpServer-2157 [000] d..2 1293.630445: cdns3_wa1: WA1: ep1out set guard |
| MtpServer-2157 [000] d..2 1293.630450: cdns3_wa1: WA1: ep1out restore cycle bit |
| MtpServer-2157 [000] d..2 1293.630453: cdns3_prepare_trb: ep1out: trb 000000007317b3ee, dma buf: 0xffd5bc00, size: 512, burst: 128 ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal) SID:0 LAST_SID:0 |
| MtpServer-2157 [000] d..2 1293.630460: cdns3_doorbell_epx: ep1out, ep_trbaddr ffd50414 |
| .... |
| irq/241-5b13000-2154 [000] d..1 1293.680849: cdns3_epx_irq: IRQ for ep1out: 01000408 ISP , ep_traddr: ffd508ac ep_last_sid: 00000000 use_streams: 0 |
| irq/241-5b13000-2154 [000] d..1 1293.680858: cdns3_complete_trb: ep1out: trb 0000000021a11b54, dma buf: 0xffd50420, size: 16384, burst: 128 ctrl: 0x00001810 (C=0, T=0, CHAIN, LINK) SID:0 LAST_SID:0 |
| irq/241-5b13000-2154 [000] d..1 1293.680865: cdns3_request_handled: Req: 00000000afbccb7d not handled, DMA pos: 185, ep deq: 88, ep enq: 185, start trb: 184, end trb: 184 |
| |
| Actually DMA pos already bigger than previous submit request afbccb7d's TRB (184-184). The reason of (not handled) is that deq position is wrong. |
| |
| The TRB link is below when irq happen. |
| |
| DEQ LINK LINK LINK LINK LINK .... TRB(afbccb7d):START DMA(EP_TRADDR). |
| |
| Original code check LINK TRB, but DEQ just move one step. |
| |
| LINK DEQ LINK LINK LINK LINK .... TRB(afbccb7d):START DMA(EP_TRADDR). |
| |
| This patch skip all LINK TRB and sync DEQ to trb's start. |
| |
| LINK LINK LINK LINK LINK .... DEQ = TRB(afbccb7d):START DMA(EP_TRADDR). |
| |
| Acked-by: Peter Chen <peter.chen@kernel.org> |
| Cc: stable <stable@vger.kernel.org> |
| Signed-off-by: Frank Li <Frank.Li@nxp.com> |
| Signed-off-by: Jun Li <jun.li@nxp.com> |
| Link: https://lore.kernel.org/r/20211130154239.8029-1-Frank.Li@nxp.com |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/usb/cdns3/cdns3-gadget.c | 20 ++++---------------- |
| 1 file changed, 4 insertions(+), 16 deletions(-) |
| |
| --- a/drivers/usb/cdns3/cdns3-gadget.c |
| +++ b/drivers/usb/cdns3/cdns3-gadget.c |
| @@ -337,19 +337,6 @@ static void cdns3_ep_inc_deq(struct cdns |
| cdns3_ep_inc_trb(&priv_ep->dequeue, &priv_ep->ccs, priv_ep->num_trbs); |
| } |
| |
| -static void cdns3_move_deq_to_next_trb(struct cdns3_request *priv_req) |
| -{ |
| - struct cdns3_endpoint *priv_ep = priv_req->priv_ep; |
| - int current_trb = priv_req->start_trb; |
| - |
| - while (current_trb != priv_req->end_trb) { |
| - cdns3_ep_inc_deq(priv_ep); |
| - current_trb = priv_ep->dequeue; |
| - } |
| - |
| - cdns3_ep_inc_deq(priv_ep); |
| -} |
| - |
| /** |
| * cdns3_allow_enable_l1 - enable/disable permits to transition to L1. |
| * @priv_dev: Extended gadget object |
| @@ -1517,10 +1504,11 @@ static void cdns3_transfer_completed(str |
| |
| trb = priv_ep->trb_pool + priv_ep->dequeue; |
| |
| - /* Request was dequeued and TRB was changed to TRB_LINK. */ |
| - if (TRB_FIELD_TO_TYPE(le32_to_cpu(trb->control)) == TRB_LINK) { |
| + /* The TRB was changed as link TRB, and the request was handled at ep_dequeue */ |
| + while (TRB_FIELD_TO_TYPE(le32_to_cpu(trb->control)) == TRB_LINK) { |
| trace_cdns3_complete_trb(priv_ep, trb); |
| - cdns3_move_deq_to_next_trb(priv_req); |
| + cdns3_ep_inc_deq(priv_ep); |
| + trb = priv_ep->trb_pool + priv_ep->dequeue; |
| } |
| |
| if (!request->stream_id) { |