* [PATCH 1/5] usb: dwc3: ep0: Don't clear ep0 DWC3_EP_TRANSFER_STARTED
2024-11-14 1:02 [PATCH 0/5] usb: dwc3: gadget: Misc fixes and cleanup Thinh Nguyen
@ 2024-11-14 1:02 ` Thinh Nguyen
2024-11-14 1:02 ` [PATCH 2/5] usb: dwc3: gadget: Fix checking for number of TRBs left Thinh Nguyen
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Thinh Nguyen @ 2024-11-14 1:02 UTC (permalink / raw)
To: Greg Kroah-Hartman, Thinh Nguyen, linux-usb@vger.kernel.org
Cc: John Youn, stable@vger.kernel.org
The driver cannot issue the End Transfer command to the SETUP transfer.
Don't clear DWC3_EP_TRANSFER_STARTED flag to make sure that the driver
won't send Start Transfer command again, which can cause no-resource
error. For example this can occur if the host issues a reset to the
device.
Cc: stable@vger.kernel.org
Fixes: 76cb323f80ac ("usb: dwc3: ep0: clear all EP0 flags")
Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
---
drivers/usb/dwc3/ep0.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index f3d97ad5156e..666ac432f52d 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -232,7 +232,7 @@ void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
/* stall is always issued on EP0 */
dep = dwc->eps[0];
__dwc3_gadget_ep_set_halt(dep, 1, false);
- dep->flags &= DWC3_EP_RESOURCE_ALLOCATED;
+ dep->flags &= DWC3_EP_RESOURCE_ALLOCATED | DWC3_EP_TRANSFER_STARTED;
dep->flags |= DWC3_EP_ENABLED;
dwc->delayed_status = false;
--
2.28.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH 2/5] usb: dwc3: gadget: Fix checking for number of TRBs left
2024-11-14 1:02 [PATCH 0/5] usb: dwc3: gadget: Misc fixes and cleanup Thinh Nguyen
2024-11-14 1:02 ` [PATCH 1/5] usb: dwc3: ep0: Don't clear ep0 DWC3_EP_TRANSFER_STARTED Thinh Nguyen
@ 2024-11-14 1:02 ` Thinh Nguyen
2024-11-14 1:02 ` [PATCH 3/5] usb: dwc3: gadget: Fix looping of queued SG entries Thinh Nguyen
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Thinh Nguyen @ 2024-11-14 1:02 UTC (permalink / raw)
To: Greg Kroah-Hartman, Thinh Nguyen, linux-usb@vger.kernel.org
Cc: John Youn, stable@vger.kernel.org
The check whether the TRB ring is full or empty in dwc3_calc_trbs_left()
is insufficient. It assumes there are active TRBs if there's any request
in the started_list. However, that's not the case for requests with a
large SG list.
That is, if we have a single usb request that requires more TRBs than
the total TRBs in the TRB ring, the queued TRBs will be available when
all the TRBs in the ring are completed. But the request is only
partially completed and remains in the started_list. With the current
logic, the TRB ring is empty, but dwc3_calc_trbs_left() returns 0.
Fix this by additionally checking for the request->num_trbs for active
TRB count.
Cc: stable@vger.kernel.org
Fixes: 51f1954ad853 ("usb: dwc3: gadget: Fix dwc3_calc_trbs_left()")
Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
---
drivers/usb/dwc3/gadget.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 6101e5467b08..38c3769a6c48 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1230,11 +1230,14 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
* pending to be processed by the driver.
*/
if (dep->trb_enqueue == dep->trb_dequeue) {
+ struct dwc3_request *req;
+
/*
- * If there is any request remained in the started_list at
- * this point, that means there is no TRB available.
+ * If there is any request remained in the started_list with
+ * active TRBs at this point, then there is no TRB available.
*/
- if (!list_empty(&dep->started_list))
+ req = next_request(&dep->started_list);
+ if (req && req->num_trbs)
return 0;
return DWC3_TRB_NUM - 1;
--
2.28.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH 3/5] usb: dwc3: gadget: Fix looping of queued SG entries
2024-11-14 1:02 [PATCH 0/5] usb: dwc3: gadget: Misc fixes and cleanup Thinh Nguyen
2024-11-14 1:02 ` [PATCH 1/5] usb: dwc3: ep0: Don't clear ep0 DWC3_EP_TRANSFER_STARTED Thinh Nguyen
2024-11-14 1:02 ` [PATCH 2/5] usb: dwc3: gadget: Fix checking for number of TRBs left Thinh Nguyen
@ 2024-11-14 1:02 ` Thinh Nguyen
2024-11-14 1:02 ` [PATCH 4/5] usb: dwc3: gadget: Cleanup SG handling Thinh Nguyen
2024-11-14 1:02 ` [PATCH 5/5] usb: dwc3: gadget: Remove dwc3_request->needs_extra_trb Thinh Nguyen
4 siblings, 0 replies; 6+ messages in thread
From: Thinh Nguyen @ 2024-11-14 1:02 UTC (permalink / raw)
To: Greg Kroah-Hartman, Thinh Nguyen, linux-usb@vger.kernel.org
Cc: John Youn, stable@vger.kernel.org
The dwc3_request->num_queued_sgs is decremented on completion. If a
partially completed request is handled, then the
dwc3_request->num_queued_sgs no longer reflects the total number of
num_queued_sgs (it would be cleared).
Correctly check the number of request SG entries remained to be prepare
and queued. Failure to do this may cause null pointer dereference when
accessing non-existent SG entry.
Cc: stable@vger.kernel.org
Fixes: c96e6725db9d ("usb: dwc3: gadget: Correct the logic for queuing sgs")
Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
---
drivers/usb/dwc3/gadget.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 38c3769a6c48..3a5a0d8be33c 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1470,8 +1470,8 @@ static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep,
struct scatterlist *s;
int i;
unsigned int length = req->request.length;
- unsigned int remaining = req->request.num_mapped_sgs
- - req->num_queued_sgs;
+ unsigned int remaining = req->num_pending_sgs;
+ unsigned int num_queued_sgs = req->request.num_mapped_sgs - remaining;
unsigned int num_trbs = req->num_trbs;
bool needs_extra_trb = dwc3_needs_extra_trb(dep, req);
@@ -1479,7 +1479,7 @@ static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep,
* If we resume preparing the request, then get the remaining length of
* the request and resume where we left off.
*/
- for_each_sg(req->request.sg, s, req->num_queued_sgs, i)
+ for_each_sg(req->request.sg, s, num_queued_sgs, i)
length -= sg_dma_len(s);
for_each_sg(sg, s, remaining, i) {
--
2.28.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH 4/5] usb: dwc3: gadget: Cleanup SG handling
2024-11-14 1:02 [PATCH 0/5] usb: dwc3: gadget: Misc fixes and cleanup Thinh Nguyen
` (2 preceding siblings ...)
2024-11-14 1:02 ` [PATCH 3/5] usb: dwc3: gadget: Fix looping of queued SG entries Thinh Nguyen
@ 2024-11-14 1:02 ` Thinh Nguyen
2024-11-14 1:02 ` [PATCH 5/5] usb: dwc3: gadget: Remove dwc3_request->needs_extra_trb Thinh Nguyen
4 siblings, 0 replies; 6+ messages in thread
From: Thinh Nguyen @ 2024-11-14 1:02 UTC (permalink / raw)
To: Greg Kroah-Hartman, Thinh Nguyen, linux-usb@vger.kernel.org; +Cc: John Youn
The current logic in dwc3 driver is tracking req->num_queued_sgs and
req->sg. But they can be checked base on the num_pending_sgs and
num_trbs. They are redundant and can complicate the SG logic. Let's
remove them.
Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
---
drivers/usb/dwc3/core.h | 3 ---
drivers/usb/dwc3/gadget.c | 42 +++++++--------------------------------
2 files changed, 7 insertions(+), 38 deletions(-)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 2dccd8fa7efd..aa09ccbf34a5 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -941,10 +941,8 @@ struct dwc3_hwparams {
* @request: struct usb_request to be transferred
* @list: a list_head used for request queueing
* @dep: struct dwc3_ep owning this request
- * @sg: pointer to first incomplete sg
* @start_sg: pointer to the sg which should be queued next
* @num_pending_sgs: counter to pending sgs
- * @num_queued_sgs: counter to the number of sgs which already got queued
* @remaining: amount of data remaining
* @status: internal dwc3 request status tracking
* @epnum: endpoint number to which this request refers
@@ -964,7 +962,6 @@ struct dwc3_request {
struct scatterlist *start_sg;
unsigned int num_pending_sgs;
- unsigned int num_queued_sgs;
unsigned int remaining;
unsigned int status;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 3a5a0d8be33c..687bb8cc4114 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1544,7 +1544,6 @@ static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep,
if (!last_sg)
req->start_sg = sg_next(s);
- req->num_queued_sgs++;
req->num_pending_sgs--;
/*
@@ -1625,9 +1624,7 @@ static int dwc3_prepare_trbs(struct dwc3_ep *dep)
if (ret)
return ret;
- req->sg = req->request.sg;
- req->start_sg = req->sg;
- req->num_queued_sgs = 0;
+ req->start_sg = req->request.sg;
req->num_pending_sgs = req->request.num_mapped_sgs;
if (req->num_pending_sgs > 0) {
@@ -3472,20 +3469,16 @@ static int dwc3_gadget_ep_reclaim_trb_sg(struct dwc3_ep *dep,
int status)
{
struct dwc3_trb *trb;
- struct scatterlist *sg = req->sg;
- struct scatterlist *s;
- unsigned int num_queued = req->num_queued_sgs;
+ unsigned int num_completed_trbs = req->num_trbs;
unsigned int i;
int ret = 0;
- for_each_sg(sg, s, num_queued, i) {
+ for (i = 0; i < num_completed_trbs; i++) {
trb = &dep->trb_pool[dep->trb_dequeue];
- req->sg = sg_next(s);
- req->num_queued_sgs--;
-
ret = dwc3_gadget_ep_reclaim_completed_trb(dep, req,
- trb, event, status, true);
+ trb, event, status,
+ !!(trb->ctrl & DWC3_TRB_CTRL_CHN));
if (ret)
break;
}
@@ -3493,19 +3486,9 @@ static int dwc3_gadget_ep_reclaim_trb_sg(struct dwc3_ep *dep,
return ret;
}
-static int dwc3_gadget_ep_reclaim_trb_linear(struct dwc3_ep *dep,
- struct dwc3_request *req, const struct dwc3_event_depevt *event,
- int status)
-{
- struct dwc3_trb *trb = &dep->trb_pool[dep->trb_dequeue];
-
- return dwc3_gadget_ep_reclaim_completed_trb(dep, req, trb,
- event, status, false);
-}
-
static bool dwc3_gadget_ep_request_completed(struct dwc3_request *req)
{
- return req->num_pending_sgs == 0 && req->num_queued_sgs == 0;
+ return req->num_pending_sgs == 0 && req->num_trbs == 0;
}
static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
@@ -3515,24 +3498,13 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
int request_status;
int ret;
- if (req->request.num_mapped_sgs)
- ret = dwc3_gadget_ep_reclaim_trb_sg(dep, req, event,
- status);
- else
- ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
- status);
+ ret = dwc3_gadget_ep_reclaim_trb_sg(dep, req, event, status);
req->request.actual = req->request.length - req->remaining;
if (!dwc3_gadget_ep_request_completed(req))
goto out;
- if (req->needs_extra_trb) {
- ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
- status);
- req->needs_extra_trb = false;
- }
-
/*
* The event status only reflects the status of the TRB with IOC set.
* For the requests that don't set interrupt on completion, the driver
--
2.28.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH 5/5] usb: dwc3: gadget: Remove dwc3_request->needs_extra_trb
2024-11-14 1:02 [PATCH 0/5] usb: dwc3: gadget: Misc fixes and cleanup Thinh Nguyen
` (3 preceding siblings ...)
2024-11-14 1:02 ` [PATCH 4/5] usb: dwc3: gadget: Cleanup SG handling Thinh Nguyen
@ 2024-11-14 1:02 ` Thinh Nguyen
4 siblings, 0 replies; 6+ messages in thread
From: Thinh Nguyen @ 2024-11-14 1:02 UTC (permalink / raw)
To: Greg Kroah-Hartman, Thinh Nguyen, linux-usb@vger.kernel.org; +Cc: John Youn
Now that we track TRBs base on request->num_trbs on reclaim, we don't
need to save the dwc3_request->needs_extra_trb check. Remove it.
Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
---
drivers/usb/dwc3/core.h | 3 ---
drivers/usb/dwc3/gadget.c | 8 ++++----
2 files changed, 4 insertions(+), 7 deletions(-)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index aa09ccbf34a5..ee73789326bc 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -949,8 +949,6 @@ struct dwc3_hwparams {
* @trb: pointer to struct dwc3_trb
* @trb_dma: DMA address of @trb
* @num_trbs: number of TRBs used by this request
- * @needs_extra_trb: true when request needs one extra TRB (either due to ZLP
- * or unaligned OUT)
* @direction: IN or OUT direction flag
* @mapped: true when request has been dma-mapped
*/
@@ -979,7 +977,6 @@ struct dwc3_request {
unsigned int num_trbs;
- unsigned int needs_extra_trb:1;
unsigned int direction:1;
unsigned int mapped:1;
};
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 687bb8cc4114..83dc7304d701 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -197,7 +197,6 @@ static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep,
list_del(&req->list);
req->remaining = 0;
- req->needs_extra_trb = false;
req->num_trbs = 0;
if (req->request.status == -EINPROGRESS)
@@ -1440,6 +1439,7 @@ static int dwc3_prepare_last_sg(struct dwc3_ep *dep,
unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
unsigned int rem = req->request.length % maxp;
unsigned int num_trbs = 1;
+ bool needs_extra_trb;
if (dwc3_needs_extra_trb(dep, req))
num_trbs++;
@@ -1447,15 +1447,15 @@ static int dwc3_prepare_last_sg(struct dwc3_ep *dep,
if (dwc3_calc_trbs_left(dep) < num_trbs)
return 0;
- req->needs_extra_trb = num_trbs > 1;
+ needs_extra_trb = num_trbs > 1;
/* Prepare a normal TRB */
if (req->direction || req->request.length)
dwc3_prepare_one_trb(dep, req, entry_length,
- req->needs_extra_trb, node, false, false);
+ needs_extra_trb, node, false, false);
/* Prepare extra TRBs for ZLP and MPS OUT transfer alignment */
- if ((!req->direction && !req->request.length) || req->needs_extra_trb)
+ if ((!req->direction && !req->request.length) || needs_extra_trb)
dwc3_prepare_one_trb(dep, req,
req->direction ? 0 : maxp - rem,
false, 1, true, false);
--
2.28.0
^ permalink raw reply related [flat|nested] 6+ messages in thread