* [PATCH v3] usb: dwc3: gadget: Prevent EP resource conflicts during StartTransfer
[not found] <CGME20260227121338epcas5p4baebb406db37f07223545b2f85751bf2@epcas5p4.samsung.com>
@ 2026-02-27 12:12 ` Selvarasu Ganesan
2026-02-28 0:27 ` Thinh Nguyen
0 siblings, 1 reply; 9+ messages in thread
From: Selvarasu Ganesan @ 2026-02-27 12:12 UTC (permalink / raw)
To: Thinh.Nguyen, gregkh, linux-usb, linux-kernel
Cc: jh0801.jung, dh10.jung, akash.m5, hongpooh.kim, eomji.oh,
h10.kim, shijie.cai, alim.akhtar, muhammed.ali, thiagu.r,
pritam.sutar, Selvarasu Ganesan, stable
The below “No resource for ep” warning appears when a StartTransfer
command is issued for bulk or interrupt endpoints in
`dwc3_gadget_ep_enable` while a previous StartTransfer on the same
endpoint is still in progress. The gadget functions drivers can invoke
`usb_ep_enable` (which triggers a new StartTransfer command) before the
earlier transfer has completed. Because the previous StartTransfer is
still active, `dwc3_gadget_ep_disable` can skip the required
`EndTransfer` due to `DWC3_EP_DELAY_STOP`, leading to the endpoint
resources are busy for previous StartTransfer and warning ("No resource
for ep") from dwc3 driver.
Additionally, a race condition exists between dwc3_gadget_ep_disable()
and dwc3_gadget_ep_queue() when manipulating dep->flags. When
dwc3_gadget_ep_disable() calls dwc3_gadget_giveback(), the dwc->lock is
temporarily released. If dwc3_gadget_ep_queue() runs in that window, it
may set the DWC3_EP_TRANSFER_STARTED flag as part of
dwc3_send_gadget_ep_cmd(). When ep_disable resumes, it unconditionally
clears all flags except those explicitly masked, potentially clearing
DWC3_EP_TRANSFER_STARTED even though a new transfer has started. This
leads to "No resource for ep" warnings on subsequent StartTransfer
attempts.
The underlying framework issue is that usb_ep_disable() is expected to
complete pending requests before returning, but is allowed to be called
from interrupt context where sleeping to wait for completion is not
possible.
As temporary workarounds for this framework limitation:
1. In __dwc3_gadget_ep_enable(), add a check for the
DWC3_EP_TRANSFER_STARTED flag before issuing a new StartTransfer.
This prevents a second StartTransfer on an already busy endpoint,
eliminating the resource conflict.
2. In __dwc3_gadget_ep_disable(), preserve the DWC3_EP_TRANSFER_STARTED
flag when masking dep->flags if it is actually set, preventing the
race with dwc3_gadget_ep_queue() from corrupting the flag state.
These changes eliminate the "No resource for ep" warnings and potential
kernel panics caused by panic_on_warn.
dwc3 13200000.dwc3: No resource for ep1out
WARNING: CPU: 0 PID: 700 at drivers/usb/dwc3/gadget.c:398 dwc3_send_gadget_ep_cmd+0x2f8/0x76c
Call trace:
dwc3_send_gadget_ep_cmd+0x2f8/0x76c
__dwc3_gadget_ep_enable+0x490/0x7c0
dwc3_gadget_ep_enable+0x6c/0xe4
usb_ep_enable+0x5c/0x15c
mp_eth_stop+0xd4/0x11c
__dev_close_many+0x160/0x1c8
__dev_change_flags+0xfc/0x220
dev_change_flags+0x24/0x70
devinet_ioctl+0x434/0x524
inet_ioctl+0xa8/0x224
sock_do_ioctl+0x74/0x128
sock_ioctl+0x3bc/0x468
__arm64_sys_ioctl+0xa8/0xe4
invoke_syscall+0x58/0x10c
el0_svc_common+0xa8/0xdc
do_el0_svc+0x1c/0x28
el0_svc+0x38/0x88
el0t_64_sync_handler+0x70/0xbc
el0t_64_sync+0x1a8/0x1ac
Cc: stable@vger.kernel.org
Signed-off-by: Selvarasu Ganesan <selvarasu.g@samsung.com>
---
Note: No Fixes tag is added because this is a workaround for the
gadget framework issue where the gadget framework calls usb_ep_disable()
in interrupt context without ensuring endpoint flushing completes.
A proper fix requires refactoring the framework to make sure
usb_ep_disable is invoked in process context.
Changes in v3:
- Revised the commit message to detail the real gadget framework issue
pointed out by the reviewer.
- Merged the two fixes for the same ep wringing into one patch.
Link to v2: https://lore.kernel.org/linux-usb/20251117155920.643-1-selvarasu.g@samsung.com/
Changes in v2:
- Removed change-id.
- Updated commit message.
Link to v1: https://lore.kernel.org/linux-usb/20251117152812.622-1-selvarasu.g@samsung.com/
---
drivers/usb/dwc3/gadget.c | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 0a688904ce8c5..3af1bbfe3d92b 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -971,8 +971,9 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
* Issue StartTransfer here with no-op TRB so we can always rely on No
* Response Update Transfer command.
*/
- if (usb_endpoint_xfer_bulk(desc) ||
- usb_endpoint_xfer_int(desc)) {
+ if ((usb_endpoint_xfer_bulk(desc) ||
+ usb_endpoint_xfer_int(desc)) &&
+ !(dep->flags & DWC3_EP_TRANSFER_STARTED)) {
struct dwc3_gadget_ep_cmd_params params;
struct dwc3_trb *trb;
dma_addr_t trb_dma;
@@ -1096,6 +1097,23 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
*/
if (dep->flags & DWC3_EP_DELAY_STOP)
mask |= (DWC3_EP_DELAY_STOP | DWC3_EP_TRANSFER_STARTED);
+
+ /*
+ * When dwc3_gadget_ep_disable() calls dwc3_gadget_giveback(),
+ * the dwc->lock is temporarily released. If dwc3_gadget_ep_queue()
+ * runs in that window it may set the DWC3_EP_TRANSFER_STARTED flag as
+ * part of dwc3_send_gadget_ep_cmd. The original code cleared the flag
+ * unconditionally in the mask operation, which could overwrite the
+ * concurrent modification.
+ *
+ * As a workaround for the interrupt context constraint where we cannot
+ * wait for endpoint flushing, preserve the DWC3_EP_TRANSFER_STARTED
+ * flag if it is set, avoiding resource conflicts until the framework
+ * is fixed to properly synchronize endpoint lifecycle management.
+ */
+ if (dep->flags & DWC3_EP_TRANSFER_STARTED)
+ mask |= DWC3_EP_TRANSFER_STARTED;
+
dep->flags &= mask;
/* Clear out the ep descriptors for non-ep0 */
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3] usb: dwc3: gadget: Prevent EP resource conflicts during StartTransfer
2026-02-27 12:12 ` [PATCH v3] usb: dwc3: gadget: Prevent EP resource conflicts during StartTransfer Selvarasu Ganesan
@ 2026-02-28 0:27 ` Thinh Nguyen
2026-03-03 0:39 ` Thinh Nguyen
0 siblings, 1 reply; 9+ messages in thread
From: Thinh Nguyen @ 2026-02-28 0:27 UTC (permalink / raw)
To: Selvarasu Ganesan
Cc: Thinh Nguyen, gregkh, linux-usb, linux-kernel, jh0801.jung,
dh10.jung, akash.m5, hongpooh.kim, eomji.oh, h10.kim, shijie.cai,
alim.akhtar, muhammed.ali, thiagu.r, pritam.sutar, stable
On Fri, Feb 27, 2026, Selvarasu Ganesan wrote:
> The below “No resource for ep” warning appears when a StartTransfer
> command is issued for bulk or interrupt endpoints in
> `dwc3_gadget_ep_enable` while a previous StartTransfer on the same
> endpoint is still in progress. The gadget functions drivers can invoke
> `usb_ep_enable` (which triggers a new StartTransfer command) before the
> earlier transfer has completed. Because the previous StartTransfer is
> still active, `dwc3_gadget_ep_disable` can skip the required
> `EndTransfer` due to `DWC3_EP_DELAY_STOP`, leading to the endpoint
> resources are busy for previous StartTransfer and warning ("No resource
> for ep") from dwc3 driver.
>
> Additionally, a race condition exists between dwc3_gadget_ep_disable()
> and dwc3_gadget_ep_queue() when manipulating dep->flags. When
> dwc3_gadget_ep_disable() calls dwc3_gadget_giveback(), the dwc->lock is
> temporarily released. If dwc3_gadget_ep_queue() runs in that window, it
> may set the DWC3_EP_TRANSFER_STARTED flag as part of
> dwc3_send_gadget_ep_cmd(). When ep_disable resumes, it unconditionally
> clears all flags except those explicitly masked, potentially clearing
> DWC3_EP_TRANSFER_STARTED even though a new transfer has started. This
> leads to "No resource for ep" warnings on subsequent StartTransfer
> attempts.
>
> The underlying framework issue is that usb_ep_disable() is expected to
> complete pending requests before returning, but is allowed to be called
> from interrupt context where sleeping to wait for completion is not
> possible.
>
> As temporary workarounds for this framework limitation:
>
> 1. In __dwc3_gadget_ep_enable(), add a check for the
> DWC3_EP_TRANSFER_STARTED flag before issuing a new StartTransfer.
> This prevents a second StartTransfer on an already busy endpoint,
> eliminating the resource conflict.
>
> 2. In __dwc3_gadget_ep_disable(), preserve the DWC3_EP_TRANSFER_STARTED
> flag when masking dep->flags if it is actually set, preventing the
> race with dwc3_gadget_ep_queue() from corrupting the flag state.
>
> These changes eliminate the "No resource for ep" warnings and potential
> kernel panics caused by panic_on_warn.
>
> dwc3 13200000.dwc3: No resource for ep1out
> WARNING: CPU: 0 PID: 700 at drivers/usb/dwc3/gadget.c:398 dwc3_send_gadget_ep_cmd+0x2f8/0x76c
> Call trace:
> dwc3_send_gadget_ep_cmd+0x2f8/0x76c
> __dwc3_gadget_ep_enable+0x490/0x7c0
> dwc3_gadget_ep_enable+0x6c/0xe4
> usb_ep_enable+0x5c/0x15c
> mp_eth_stop+0xd4/0x11c
> __dev_close_many+0x160/0x1c8
> __dev_change_flags+0xfc/0x220
> dev_change_flags+0x24/0x70
> devinet_ioctl+0x434/0x524
> inet_ioctl+0xa8/0x224
> sock_do_ioctl+0x74/0x128
> sock_ioctl+0x3bc/0x468
> __arm64_sys_ioctl+0xa8/0xe4
> invoke_syscall+0x58/0x10c
> el0_svc_common+0xa8/0xdc
> do_el0_svc+0x1c/0x28
> el0_svc+0x38/0x88
> el0t_64_sync_handler+0x70/0xbc
> el0t_64_sync+0x1a8/0x1ac
>
> Cc: stable@vger.kernel.org
> Signed-off-by: Selvarasu Ganesan <selvarasu.g@samsung.com>
> ---
>
> Note: No Fixes tag is added because this is a workaround for the
> gadget framework issue where the gadget framework calls usb_ep_disable()
> in interrupt context without ensuring endpoint flushing completes.
> A proper fix requires refactoring the framework to make sure
> usb_ep_disable is invoked in process context.
>
> Changes in v3:
> - Revised the commit message to detail the real gadget framework issue
> pointed out by the reviewer.
> - Merged the two fixes for the same ep wringing into one patch.
> Link to v2: https://urldefense.com/v3/__https://lore.kernel.org/linux-usb/20251117155920.643-1-selvarasu.g@samsung.com/__;!!A4F2R9G_pg!cQzQQ5kAWF6CE5hQe7VqFdnaxqwzsTB1ZGNT1GvCH28GoB_nESZR5Y2jtxdZBls6wBIM4OtpvG4dSaylvNC3qbh547k$
>
> Changes in v2:
> - Removed change-id.
> - Updated commit message.
> Link to v1: https://urldefense.com/v3/__https://lore.kernel.org/linux-usb/20251117152812.622-1-selvarasu.g@samsung.com/__;!!A4F2R9G_pg!cQzQQ5kAWF6CE5hQe7VqFdnaxqwzsTB1ZGNT1GvCH28GoB_nESZR5Y2jtxdZBls6wBIM4OtpvG4dSaylvNC38z-CRD4$
> ---
> drivers/usb/dwc3/gadget.c | 22 ++++++++++++++++++++--
> 1 file changed, 20 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
> index 0a688904ce8c5..3af1bbfe3d92b 100644
> --- a/drivers/usb/dwc3/gadget.c
> +++ b/drivers/usb/dwc3/gadget.c
> @@ -971,8 +971,9 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
> * Issue StartTransfer here with no-op TRB so we can always rely on No
> * Response Update Transfer command.
> */
> - if (usb_endpoint_xfer_bulk(desc) ||
> - usb_endpoint_xfer_int(desc)) {
> + if ((usb_endpoint_xfer_bulk(desc) ||
> + usb_endpoint_xfer_int(desc)) &&
> + !(dep->flags & DWC3_EP_TRANSFER_STARTED)) {
> struct dwc3_gadget_ep_cmd_params params;
> struct dwc3_trb *trb;
> dma_addr_t trb_dma;
> @@ -1096,6 +1097,23 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
> */
> if (dep->flags & DWC3_EP_DELAY_STOP)
> mask |= (DWC3_EP_DELAY_STOP | DWC3_EP_TRANSFER_STARTED);
> +
> + /*
> + * When dwc3_gadget_ep_disable() calls dwc3_gadget_giveback(),
> + * the dwc->lock is temporarily released. If dwc3_gadget_ep_queue()
> + * runs in that window it may set the DWC3_EP_TRANSFER_STARTED flag as
> + * part of dwc3_send_gadget_ep_cmd. The original code cleared the flag
> + * unconditionally in the mask operation, which could overwrite the
> + * concurrent modification.
> + *
> + * As a workaround for the interrupt context constraint where we cannot
> + * wait for endpoint flushing, preserve the DWC3_EP_TRANSFER_STARTED
> + * flag if it is set, avoiding resource conflicts until the framework
> + * is fixed to properly synchronize endpoint lifecycle management.
> + */
> + if (dep->flags & DWC3_EP_TRANSFER_STARTED)
> + mask |= DWC3_EP_TRANSFER_STARTED;
> +
> dep->flags &= mask;
>
> /* Clear out the ep descriptors for non-ep0 */
> --
> 2.34.1
>
Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Thanks!
Thinh
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3] usb: dwc3: gadget: Prevent EP resource conflicts during StartTransfer
2026-02-28 0:27 ` Thinh Nguyen
@ 2026-03-03 0:39 ` Thinh Nguyen
2026-03-06 13:06 ` Selvarasu Ganesan
0 siblings, 1 reply; 9+ messages in thread
From: Thinh Nguyen @ 2026-03-03 0:39 UTC (permalink / raw)
To: Selvarasu Ganesan
Cc: gregkh, linux-usb, linux-kernel, jh0801.jung, dh10.jung,
akash.m5, hongpooh.kim, eomji.oh, h10.kim, shijie.cai,
alim.akhtar, muhammed.ali, thiagu.r, pritam.sutar, stable
On Sat, Feb 28, 2026, Thinh Nguyen wrote:
> On Fri, Feb 27, 2026, Selvarasu Ganesan wrote:
> > The below “No resource for ep” warning appears when a StartTransfer
> > command is issued for bulk or interrupt endpoints in
> > `dwc3_gadget_ep_enable` while a previous StartTransfer on the same
> > endpoint is still in progress. The gadget functions drivers can invoke
> > `usb_ep_enable` (which triggers a new StartTransfer command) before the
> > earlier transfer has completed. Because the previous StartTransfer is
> > still active, `dwc3_gadget_ep_disable` can skip the required
> > `EndTransfer` due to `DWC3_EP_DELAY_STOP`, leading to the endpoint
> > resources are busy for previous StartTransfer and warning ("No resource
> > for ep") from dwc3 driver.
> >
> > Additionally, a race condition exists between dwc3_gadget_ep_disable()
> > and dwc3_gadget_ep_queue() when manipulating dep->flags. When
> > dwc3_gadget_ep_disable() calls dwc3_gadget_giveback(), the dwc->lock is
> > temporarily released. If dwc3_gadget_ep_queue() runs in that window, it
> > may set the DWC3_EP_TRANSFER_STARTED flag as part of
> > dwc3_send_gadget_ep_cmd(). When ep_disable resumes, it unconditionally
> > clears all flags except those explicitly masked, potentially clearing
> > DWC3_EP_TRANSFER_STARTED even though a new transfer has started. This
> > leads to "No resource for ep" warnings on subsequent StartTransfer
> > attempts.
> >
> > The underlying framework issue is that usb_ep_disable() is expected to
> > complete pending requests before returning, but is allowed to be called
> > from interrupt context where sleeping to wait for completion is not
> > possible.
> >
> > As temporary workarounds for this framework limitation:
> >
> > 1. In __dwc3_gadget_ep_enable(), add a check for the
> > DWC3_EP_TRANSFER_STARTED flag before issuing a new StartTransfer.
> > This prevents a second StartTransfer on an already busy endpoint,
> > eliminating the resource conflict.
> >
> > 2. In __dwc3_gadget_ep_disable(), preserve the DWC3_EP_TRANSFER_STARTED
> > flag when masking dep->flags if it is actually set, preventing the
> > race with dwc3_gadget_ep_queue() from corrupting the flag state.
> >
> > These changes eliminate the "No resource for ep" warnings and potential
> > kernel panics caused by panic_on_warn.
> >
> > dwc3 13200000.dwc3: No resource for ep1out
> > WARNING: CPU: 0 PID: 700 at drivers/usb/dwc3/gadget.c:398 dwc3_send_gadget_ep_cmd+0x2f8/0x76c
> > Call trace:
> > dwc3_send_gadget_ep_cmd+0x2f8/0x76c
> > __dwc3_gadget_ep_enable+0x490/0x7c0
> > dwc3_gadget_ep_enable+0x6c/0xe4
> > usb_ep_enable+0x5c/0x15c
> > mp_eth_stop+0xd4/0x11c
> > __dev_close_many+0x160/0x1c8
> > __dev_change_flags+0xfc/0x220
> > dev_change_flags+0x24/0x70
> > devinet_ioctl+0x434/0x524
> > inet_ioctl+0xa8/0x224
> > sock_do_ioctl+0x74/0x128
> > sock_ioctl+0x3bc/0x468
> > __arm64_sys_ioctl+0xa8/0xe4
> > invoke_syscall+0x58/0x10c
> > el0_svc_common+0xa8/0xdc
> > do_el0_svc+0x1c/0x28
> > el0_svc+0x38/0x88
> > el0t_64_sync_handler+0x70/0xbc
> > el0t_64_sync+0x1a8/0x1ac
> >
> > Cc: stable@vger.kernel.org
> > Signed-off-by: Selvarasu Ganesan <selvarasu.g@samsung.com>
> > ---
> >
> > Note: No Fixes tag is added because this is a workaround for the
> > gadget framework issue where the gadget framework calls usb_ep_disable()
> > in interrupt context without ensuring endpoint flushing completes.
> > A proper fix requires refactoring the framework to make sure
> > usb_ep_disable is invoked in process context.
> >
> > Changes in v3:
> > - Revised the commit message to detail the real gadget framework issue
> > pointed out by the reviewer.
> > - Merged the two fixes for the same ep wringing into one patch.
> > Link to v2: https://urldefense.com/v3/__https://lore.kernel.org/linux-usb/20251117155920.643-1-selvarasu.g@samsung.com/__;!!A4F2R9G_pg!cQzQQ5kAWF6CE5hQe7VqFdnaxqwzsTB1ZGNT1GvCH28GoB_nESZR5Y2jtxdZBls6wBIM4OtpvG4dSaylvNC3qbh547k$
> >
> > Changes in v2:
> > - Removed change-id.
> > - Updated commit message.
> > Link to v1: https://urldefense.com/v3/__https://lore.kernel.org/linux-usb/20251117152812.622-1-selvarasu.g@samsung.com/__;!!A4F2R9G_pg!cQzQQ5kAWF6CE5hQe7VqFdnaxqwzsTB1ZGNT1GvCH28GoB_nESZR5Y2jtxdZBls6wBIM4OtpvG4dSaylvNC38z-CRD4$
> > ---
> > drivers/usb/dwc3/gadget.c | 22 ++++++++++++++++++++--
> > 1 file changed, 20 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
> > index 0a688904ce8c5..3af1bbfe3d92b 100644
> > --- a/drivers/usb/dwc3/gadget.c
> > +++ b/drivers/usb/dwc3/gadget.c
> > @@ -971,8 +971,9 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
> > * Issue StartTransfer here with no-op TRB so we can always rely on No
> > * Response Update Transfer command.
> > */
> > - if (usb_endpoint_xfer_bulk(desc) ||
> > - usb_endpoint_xfer_int(desc)) {
> > + if ((usb_endpoint_xfer_bulk(desc) ||
> > + usb_endpoint_xfer_int(desc)) &&
> > + !(dep->flags & DWC3_EP_TRANSFER_STARTED)) {
> > struct dwc3_gadget_ep_cmd_params params;
> > struct dwc3_trb *trb;
> > dma_addr_t trb_dma;
> > @@ -1096,6 +1097,23 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
> > */
> > if (dep->flags & DWC3_EP_DELAY_STOP)
> > mask |= (DWC3_EP_DELAY_STOP | DWC3_EP_TRANSFER_STARTED);
> > +
> > + /*
> > + * When dwc3_gadget_ep_disable() calls dwc3_gadget_giveback(),
> > + * the dwc->lock is temporarily released. If dwc3_gadget_ep_queue()
> > + * runs in that window it may set the DWC3_EP_TRANSFER_STARTED flag as
> > + * part of dwc3_send_gadget_ep_cmd. The original code cleared the flag
> > + * unconditionally in the mask operation, which could overwrite the
> > + * concurrent modification.
> > + *
> > + * As a workaround for the interrupt context constraint where we cannot
> > + * wait for endpoint flushing, preserve the DWC3_EP_TRANSFER_STARTED
> > + * flag if it is set, avoiding resource conflicts until the framework
> > + * is fixed to properly synchronize endpoint lifecycle management.
> > + */
> > + if (dep->flags & DWC3_EP_TRANSFER_STARTED)
> > + mask |= DWC3_EP_TRANSFER_STARTED;
> > +
> > dep->flags &= mask;
> >
> > /* Clear out the ep descriptors for non-ep0 */
> > --
> > 2.34.1
> >
>
> Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
>
Oh wait, don't pick this patch up yet.
This will cause a regression for UAS device. When switching alt-setting
interface for BOT to UASP, the device needs to issue a Start Transfer
command.
This workaround won't work. Can we fix the usb_ep_disable() interface
and rework this instead?
BR,
Thinh
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3] usb: dwc3: gadget: Prevent EP resource conflicts during StartTransfer
2026-03-03 0:39 ` Thinh Nguyen
@ 2026-03-06 13:06 ` Selvarasu Ganesan
2026-03-06 21:41 ` Thinh Nguyen
0 siblings, 1 reply; 9+ messages in thread
From: Selvarasu Ganesan @ 2026-03-06 13:06 UTC (permalink / raw)
To: Thinh Nguyen
Cc: gregkh, linux-usb, linux-kernel, jh0801.jung, dh10.jung,
akash.m5, hongpooh.kim, eomji.oh, h10.kim, shijie.cai,
alim.akhtar, muhammed.ali, thiagu.r, pritam.sutar, stable
On 3/3/2026 6:09 AM, Thinh Nguyen wrote:
> On Sat, Feb 28, 2026, Thinh Nguyen wrote:
>> On Fri, Feb 27, 2026, Selvarasu Ganesan wrote:
>>> The below “No resource for ep” warning appears when a StartTransfer
>>> command is issued for bulk or interrupt endpoints in
>>> `dwc3_gadget_ep_enable` while a previous StartTransfer on the same
>>> endpoint is still in progress. The gadget functions drivers can invoke
>>> `usb_ep_enable` (which triggers a new StartTransfer command) before the
>>> earlier transfer has completed. Because the previous StartTransfer is
>>> still active, `dwc3_gadget_ep_disable` can skip the required
>>> `EndTransfer` due to `DWC3_EP_DELAY_STOP`, leading to the endpoint
>>> resources are busy for previous StartTransfer and warning ("No resource
>>> for ep") from dwc3 driver.
>>>
>>> Additionally, a race condition exists between dwc3_gadget_ep_disable()
>>> and dwc3_gadget_ep_queue() when manipulating dep->flags. When
>>> dwc3_gadget_ep_disable() calls dwc3_gadget_giveback(), the dwc->lock is
>>> temporarily released. If dwc3_gadget_ep_queue() runs in that window, it
>>> may set the DWC3_EP_TRANSFER_STARTED flag as part of
>>> dwc3_send_gadget_ep_cmd(). When ep_disable resumes, it unconditionally
>>> clears all flags except those explicitly masked, potentially clearing
>>> DWC3_EP_TRANSFER_STARTED even though a new transfer has started. This
>>> leads to "No resource for ep" warnings on subsequent StartTransfer
>>> attempts.
>>>
>>> The underlying framework issue is that usb_ep_disable() is expected to
>>> complete pending requests before returning, but is allowed to be called
>>> from interrupt context where sleeping to wait for completion is not
>>> possible.
>>>
>>> As temporary workarounds for this framework limitation:
>>>
>>> 1. In __dwc3_gadget_ep_enable(), add a check for the
>>> DWC3_EP_TRANSFER_STARTED flag before issuing a new StartTransfer.
>>> This prevents a second StartTransfer on an already busy endpoint,
>>> eliminating the resource conflict.
>>>
>>> 2. In __dwc3_gadget_ep_disable(), preserve the DWC3_EP_TRANSFER_STARTED
>>> flag when masking dep->flags if it is actually set, preventing the
>>> race with dwc3_gadget_ep_queue() from corrupting the flag state.
>>>
>>> These changes eliminate the "No resource for ep" warnings and potential
>>> kernel panics caused by panic_on_warn.
>>>
>>> dwc3 13200000.dwc3: No resource for ep1out
>>> WARNING: CPU: 0 PID: 700 at drivers/usb/dwc3/gadget.c:398 dwc3_send_gadget_ep_cmd+0x2f8/0x76c
>>> Call trace:
>>> dwc3_send_gadget_ep_cmd+0x2f8/0x76c
>>> __dwc3_gadget_ep_enable+0x490/0x7c0
>>> dwc3_gadget_ep_enable+0x6c/0xe4
>>> usb_ep_enable+0x5c/0x15c
>>> mp_eth_stop+0xd4/0x11c
>>> __dev_close_many+0x160/0x1c8
>>> __dev_change_flags+0xfc/0x220
>>> dev_change_flags+0x24/0x70
>>> devinet_ioctl+0x434/0x524
>>> inet_ioctl+0xa8/0x224
>>> sock_do_ioctl+0x74/0x128
>>> sock_ioctl+0x3bc/0x468
>>> __arm64_sys_ioctl+0xa8/0xe4
>>> invoke_syscall+0x58/0x10c
>>> el0_svc_common+0xa8/0xdc
>>> do_el0_svc+0x1c/0x28
>>> el0_svc+0x38/0x88
>>> el0t_64_sync_handler+0x70/0xbc
>>> el0t_64_sync+0x1a8/0x1ac
>>>
>>> Cc: stable@vger.kernel.org
>>> Signed-off-by: Selvarasu Ganesan <selvarasu.g@samsung.com>
>>> ---
>>>
>>> Note: No Fixes tag is added because this is a workaround for the
>>> gadget framework issue where the gadget framework calls usb_ep_disable()
>>> in interrupt context without ensuring endpoint flushing completes.
>>> A proper fix requires refactoring the framework to make sure
>>> usb_ep_disable is invoked in process context.
>>>
>>> Changes in v3:
>>> - Revised the commit message to detail the real gadget framework issue
>>> pointed out by the reviewer.
>>> - Merged the two fixes for the same ep wringing into one patch.
>>> Link to v2: https://urldefense.com/v3/__https://lore.kernel.org/linux-usb/20251117155920.643-1-selvarasu.g@samsung.com/__;!!A4F2R9G_pg!cQzQQ5kAWF6CE5hQe7VqFdnaxqwzsTB1ZGNT1GvCH28GoB_nESZR5Y2jtxdZBls6wBIM4OtpvG4dSaylvNC3qbh547k$
>>>
>>> Changes in v2:
>>> - Removed change-id.
>>> - Updated commit message.
>>> Link to v1: https://urldefense.com/v3/__https://lore.kernel.org/linux-usb/20251117152812.622-1-selvarasu.g@samsung.com/__;!!A4F2R9G_pg!cQzQQ5kAWF6CE5hQe7VqFdnaxqwzsTB1ZGNT1GvCH28GoB_nESZR5Y2jtxdZBls6wBIM4OtpvG4dSaylvNC38z-CRD4$
>>> ---
>>> drivers/usb/dwc3/gadget.c | 22 ++++++++++++++++++++--
>>> 1 file changed, 20 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
>>> index 0a688904ce8c5..3af1bbfe3d92b 100644
>>> --- a/drivers/usb/dwc3/gadget.c
>>> +++ b/drivers/usb/dwc3/gadget.c
>>> @@ -971,8 +971,9 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
>>> * Issue StartTransfer here with no-op TRB so we can always rely on No
>>> * Response Update Transfer command.
>>> */
>>> - if (usb_endpoint_xfer_bulk(desc) ||
>>> - usb_endpoint_xfer_int(desc)) {
>>> + if ((usb_endpoint_xfer_bulk(desc) ||
>>> + usb_endpoint_xfer_int(desc)) &&
>>> + !(dep->flags & DWC3_EP_TRANSFER_STARTED)) {
>>> struct dwc3_gadget_ep_cmd_params params;
>>> struct dwc3_trb *trb;
>>> dma_addr_t trb_dma;
>>> @@ -1096,6 +1097,23 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
>>> */
>>> if (dep->flags & DWC3_EP_DELAY_STOP)
>>> mask |= (DWC3_EP_DELAY_STOP | DWC3_EP_TRANSFER_STARTED);
>>> +
>>> + /*
>>> + * When dwc3_gadget_ep_disable() calls dwc3_gadget_giveback(),
>>> + * the dwc->lock is temporarily released. If dwc3_gadget_ep_queue()
>>> + * runs in that window it may set the DWC3_EP_TRANSFER_STARTED flag as
>>> + * part of dwc3_send_gadget_ep_cmd. The original code cleared the flag
>>> + * unconditionally in the mask operation, which could overwrite the
>>> + * concurrent modification.
>>> + *
>>> + * As a workaround for the interrupt context constraint where we cannot
>>> + * wait for endpoint flushing, preserve the DWC3_EP_TRANSFER_STARTED
>>> + * flag if it is set, avoiding resource conflicts until the framework
>>> + * is fixed to properly synchronize endpoint lifecycle management.
>>> + */
>>> + if (dep->flags & DWC3_EP_TRANSFER_STARTED)
>>> + mask |= DWC3_EP_TRANSFER_STARTED;
>>> +
>>> dep->flags &= mask;
>>>
>>> /* Clear out the ep descriptors for non-ep0 */
>>> --
>>> 2.34.1
>>>
>> Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
>>
> Oh wait, don't pick this patch up yet.
>
> This will cause a regression for UAS device. When switching alt-setting
> interface for BOT to UASP, the device needs to issue a Start Transfer
> command.
>
> This workaround won't work. Can we fix the usb_ep_disable() interface
> and rework this instead?
>
> BR,
> Thinh
Hi Thinh,
We’re trying to see how this change could cause a regression for UAS
devices.
Could you explain why the workaround might be a problem for UAS? Are you
concerned that it could miss a valid StartTransfer when a previous
transfer finishes later than expected as part of ep_disable?
If we don’t use this temporary fix, the driver can still report “EP
resource busy” when an earlier StartTransfer hasn’t finished
before ep_disable returns. That can happen when a UAS device needs to
start a new transfer during ep_enable while the prior transfer is still
pending.
The patch simply blocks a second StartTransfer when the same endpoint
already has a transfer in progress to prevent a “EP resource busy” issue.
And it can cause a new StartTransfer to be issued later from ep_queue
while the starttransfer that should have been started during ep_enable
is skipped.
Thanks,
Selva
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3] usb: dwc3: gadget: Prevent EP resource conflicts during StartTransfer
2026-03-06 13:06 ` Selvarasu Ganesan
@ 2026-03-06 21:41 ` Thinh Nguyen
2026-09-24 12:05 ` Selvarasu Ganesan
0 siblings, 1 reply; 9+ messages in thread
From: Thinh Nguyen @ 2026-03-06 21:41 UTC (permalink / raw)
To: Selvarasu Ganesan
Cc: Thinh Nguyen, gregkh, linux-usb, linux-kernel, jh0801.jung,
dh10.jung, akash.m5, hongpooh.kim, eomji.oh, h10.kim, shijie.cai,
alim.akhtar, muhammed.ali, thiagu.r, pritam.sutar, stable
On Fri, Mar 06, 2026, Selvarasu Ganesan wrote:
>
> On 3/3/2026 6:09 AM, Thinh Nguyen wrote:
> > On Sat, Feb 28, 2026, Thinh Nguyen wrote:
> >> On Fri, Feb 27, 2026, Selvarasu Ganesan wrote:
> >>> The below “No resource for ep” warning appears when a StartTransfer
> >>> command is issued for bulk or interrupt endpoints in
> >>> `dwc3_gadget_ep_enable` while a previous StartTransfer on the same
> >>> endpoint is still in progress. The gadget functions drivers can invoke
> >>> `usb_ep_enable` (which triggers a new StartTransfer command) before the
> >>> earlier transfer has completed. Because the previous StartTransfer is
> >>> still active, `dwc3_gadget_ep_disable` can skip the required
> >>> `EndTransfer` due to `DWC3_EP_DELAY_STOP`, leading to the endpoint
> >>> resources are busy for previous StartTransfer and warning ("No resource
> >>> for ep") from dwc3 driver.
> >>>
> >>> Additionally, a race condition exists between dwc3_gadget_ep_disable()
> >>> and dwc3_gadget_ep_queue() when manipulating dep->flags. When
> >>> dwc3_gadget_ep_disable() calls dwc3_gadget_giveback(), the dwc->lock is
> >>> temporarily released. If dwc3_gadget_ep_queue() runs in that window, it
> >>> may set the DWC3_EP_TRANSFER_STARTED flag as part of
> >>> dwc3_send_gadget_ep_cmd(). When ep_disable resumes, it unconditionally
> >>> clears all flags except those explicitly masked, potentially clearing
> >>> DWC3_EP_TRANSFER_STARTED even though a new transfer has started. This
> >>> leads to "No resource for ep" warnings on subsequent StartTransfer
> >>> attempts.
> >>>
> >>> The underlying framework issue is that usb_ep_disable() is expected to
> >>> complete pending requests before returning, but is allowed to be called
> >>> from interrupt context where sleeping to wait for completion is not
> >>> possible.
> >>>
> >>> As temporary workarounds for this framework limitation:
> >>>
> >>> 1. In __dwc3_gadget_ep_enable(), add a check for the
> >>> DWC3_EP_TRANSFER_STARTED flag before issuing a new StartTransfer.
> >>> This prevents a second StartTransfer on an already busy endpoint,
> >>> eliminating the resource conflict.
> >>>
> >>> 2. In __dwc3_gadget_ep_disable(), preserve the DWC3_EP_TRANSFER_STARTED
> >>> flag when masking dep->flags if it is actually set, preventing the
> >>> race with dwc3_gadget_ep_queue() from corrupting the flag state.
> >>>
> >>> These changes eliminate the "No resource for ep" warnings and potential
> >>> kernel panics caused by panic_on_warn.
> >>>
> >>> dwc3 13200000.dwc3: No resource for ep1out
> >>> WARNING: CPU: 0 PID: 700 at drivers/usb/dwc3/gadget.c:398 dwc3_send_gadget_ep_cmd+0x2f8/0x76c
> >>> Call trace:
> >>> dwc3_send_gadget_ep_cmd+0x2f8/0x76c
> >>> __dwc3_gadget_ep_enable+0x490/0x7c0
> >>> dwc3_gadget_ep_enable+0x6c/0xe4
> >>> usb_ep_enable+0x5c/0x15c
> >>> mp_eth_stop+0xd4/0x11c
> >>> __dev_close_many+0x160/0x1c8
> >>> __dev_change_flags+0xfc/0x220
> >>> dev_change_flags+0x24/0x70
> >>> devinet_ioctl+0x434/0x524
> >>> inet_ioctl+0xa8/0x224
> >>> sock_do_ioctl+0x74/0x128
> >>> sock_ioctl+0x3bc/0x468
> >>> __arm64_sys_ioctl+0xa8/0xe4
> >>> invoke_syscall+0x58/0x10c
> >>> el0_svc_common+0xa8/0xdc
> >>> do_el0_svc+0x1c/0x28
> >>> el0_svc+0x38/0x88
> >>> el0t_64_sync_handler+0x70/0xbc
> >>> el0t_64_sync+0x1a8/0x1ac
> >>>
> >>> Cc: stable@vger.kernel.org
> >>> Signed-off-by: Selvarasu Ganesan <selvarasu.g@samsung.com>
> >>> ---
> >>>
> >>> Note: No Fixes tag is added because this is a workaround for the
> >>> gadget framework issue where the gadget framework calls usb_ep_disable()
> >>> in interrupt context without ensuring endpoint flushing completes.
> >>> A proper fix requires refactoring the framework to make sure
> >>> usb_ep_disable is invoked in process context.
> >>>
> >>> Changes in v3:
> >>> - Revised the commit message to detail the real gadget framework issue
> >>> pointed out by the reviewer.
> >>> - Merged the two fixes for the same ep wringing into one patch.
> >>> Link to v2: https://urldefense.com/v3/__https://lore.kernel.org/linux-usb/20251117155920.643-1-selvarasu.g@samsung.com/__;!!A4F2R9G_pg!cQzQQ5kAWF6CE5hQe7VqFdnaxqwzsTB1ZGNT1GvCH28GoB_nESZR5Y2jtxdZBls6wBIM4OtpvG4dSaylvNC3qbh547k$
> >>>
> >>> Changes in v2:
> >>> - Removed change-id.
> >>> - Updated commit message.
> >>> Link to v1: https://urldefense.com/v3/__https://lore.kernel.org/linux-usb/20251117152812.622-1-selvarasu.g@samsung.com/__;!!A4F2R9G_pg!cQzQQ5kAWF6CE5hQe7VqFdnaxqwzsTB1ZGNT1GvCH28GoB_nESZR5Y2jtxdZBls6wBIM4OtpvG4dSaylvNC38z-CRD4$
> >>> ---
> >>> drivers/usb/dwc3/gadget.c | 22 ++++++++++++++++++++--
> >>> 1 file changed, 20 insertions(+), 2 deletions(-)
> >>>
> >>> diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
> >>> index 0a688904ce8c5..3af1bbfe3d92b 100644
> >>> --- a/drivers/usb/dwc3/gadget.c
> >>> +++ b/drivers/usb/dwc3/gadget.c
> >>> @@ -971,8 +971,9 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
> >>> * Issue StartTransfer here with no-op TRB so we can always rely on No
> >>> * Response Update Transfer command.
> >>> */
> >>> - if (usb_endpoint_xfer_bulk(desc) ||
> >>> - usb_endpoint_xfer_int(desc)) {
> >>> + if ((usb_endpoint_xfer_bulk(desc) ||
> >>> + usb_endpoint_xfer_int(desc)) &&
> >>> + !(dep->flags & DWC3_EP_TRANSFER_STARTED)) {
> >>> struct dwc3_gadget_ep_cmd_params params;
> >>> struct dwc3_trb *trb;
> >>> dma_addr_t trb_dma;
> >>> @@ -1096,6 +1097,23 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
> >>> */
> >>> if (dep->flags & DWC3_EP_DELAY_STOP)
> >>> mask |= (DWC3_EP_DELAY_STOP | DWC3_EP_TRANSFER_STARTED);
> >>> +
> >>> + /*
> >>> + * When dwc3_gadget_ep_disable() calls dwc3_gadget_giveback(),
> >>> + * the dwc->lock is temporarily released. If dwc3_gadget_ep_queue()
> >>> + * runs in that window it may set the DWC3_EP_TRANSFER_STARTED flag as
> >>> + * part of dwc3_send_gadget_ep_cmd. The original code cleared the flag
> >>> + * unconditionally in the mask operation, which could overwrite the
> >>> + * concurrent modification.
> >>> + *
> >>> + * As a workaround for the interrupt context constraint where we cannot
> >>> + * wait for endpoint flushing, preserve the DWC3_EP_TRANSFER_STARTED
> >>> + * flag if it is set, avoiding resource conflicts until the framework
> >>> + * is fixed to properly synchronize endpoint lifecycle management.
> >>> + */
> >>> + if (dep->flags & DWC3_EP_TRANSFER_STARTED)
> >>> + mask |= DWC3_EP_TRANSFER_STARTED;
> >>> +
> >>> dep->flags &= mask;
> >>>
> >>> /* Clear out the ep descriptors for non-ep0 */
> >>> --
> >>> 2.34.1
> >>>
> >> Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
> >>
> > Oh wait, don't pick this patch up yet.
> >
> > This will cause a regression for UAS device. When switching alt-setting
> > interface for BOT to UASP, the device needs to issue a Start Transfer
> > command.
> >
> > This workaround won't work. Can we fix the usb_ep_disable() interface
> > and rework this instead?
> >
> > BR,
> > Thinh
>
> Hi Thinh,
>
> We’re trying to see how this change could cause a regression for UAS
> devices.
> Could you explain why the workaround might be a problem for UAS? Are you
> concerned that it could miss a valid StartTransfer when a previous
> transfer finishes later than expected as part of ep_disable?
In UAS, the device controller uses the first PRIME to synchronize with
the host to determine whether the Start Transfer command can initiate
the stream. After configuring an endpoint, if we issue the StartTransfer
command too late, then the device controller may not initiate the
transfer (sending ERDY), and host will not know when to start the
transfer.
So we have this workaround that we would have the device Start and Stop
the endpoint immediately just to arm the endpoint for UASP transfers. In
the newer IPs, this workaround may not be needed.
>
> If we don’t use this temporary fix, the driver can still report “EP
> resource busy” when an earlier StartTransfer hasn’t finished
> before ep_disable returns. That can happen when a UAS device needs to
> start a new transfer during ep_enable while the prior transfer is still
> pending.
>
> The patch simply blocks a second StartTransfer when the same endpoint
> already has a transfer in progress to prevent a “EP resource busy” issue.
>
> And it can cause a new StartTransfer to be issued later from ep_queue
> while the starttransfer that should have been started during ep_enable
> is skipped.
>
Also, if the gadget driver just uses usb_ep_disable() to handle the
teardown instead of proactively dequeuing all the active requests, then
the DWC3_EP_TRANSFER_STARTED flag will still be cleared immediately on
usb_ep_disable() and we will still run into this issue again.
Another workaround is to have the dwc3 driver retry the command after a
small delay if there's no resource error report. Only do dev_WARN after
a few times of the same failure.
Ideally, we should fix the usb_ep_disable() and have the composite
framework properly handle the "wait" for completion.
BR,
Thinh
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3] usb: dwc3: gadget: Prevent EP resource conflicts during StartTransfer
2026-03-06 21:41 ` Thinh Nguyen
@ 2026-09-24 12:05 ` Selvarasu Ganesan
2026-09-24 12:24 ` Selvarasu Ganesan
2026-09-29 3:08 ` Thinh Nguyen
0 siblings, 2 replies; 9+ messages in thread
From: Selvarasu Ganesan @ 2026-09-24 12:05 UTC (permalink / raw)
To: Thinh Nguyen
Cc: gregkh, linux-usb, linux-kernel, jh0801.jung, dh10.jung,
akash.m5, hongpooh.kim, eomji.oh, h10.kim, shijie.cai,
alim.akhtar, muhammed.ali, thiagu.r, pritam.sutar, stable
On 3/7/2026 3:11 AM, Thinh Nguyen wrote:
> On Fri, Mar 06, 2026, Selvarasu Ganesan wrote:
>> On 3/3/2026 6:09 AM, Thinh Nguyen wrote:
>>> On Sat, Feb 28, 2026, Thinh Nguyen wrote:
>>>> On Fri, Feb 27, 2026, Selvarasu Ganesan wrote:
>>>>> The below “No resource for ep” warning appears when a StartTransfer
>>>>> command is issued for bulk or interrupt endpoints in
>>>>> `dwc3_gadget_ep_enable` while a previous StartTransfer on the same
>>>>> endpoint is still in progress. The gadget functions drivers can invoke
>>>>> `usb_ep_enable` (which triggers a new StartTransfer command) before the
>>>>> earlier transfer has completed. Because the previous StartTransfer is
>>>>> still active, `dwc3_gadget_ep_disable` can skip the required
>>>>> `EndTransfer` due to `DWC3_EP_DELAY_STOP`, leading to the endpoint
>>>>> resources are busy for previous StartTransfer and warning ("No resource
>>>>> for ep") from dwc3 driver.
>>>>>
>>>>> Additionally, a race condition exists between dwc3_gadget_ep_disable()
>>>>> and dwc3_gadget_ep_queue() when manipulating dep->flags. When
>>>>> dwc3_gadget_ep_disable() calls dwc3_gadget_giveback(), the dwc->lock is
>>>>> temporarily released. If dwc3_gadget_ep_queue() runs in that window, it
>>>>> may set the DWC3_EP_TRANSFER_STARTED flag as part of
>>>>> dwc3_send_gadget_ep_cmd(). When ep_disable resumes, it unconditionally
>>>>> clears all flags except those explicitly masked, potentially clearing
>>>>> DWC3_EP_TRANSFER_STARTED even though a new transfer has started. This
>>>>> leads to "No resource for ep" warnings on subsequent StartTransfer
>>>>> attempts.
>>>>>
>>>>> The underlying framework issue is that usb_ep_disable() is expected to
>>>>> complete pending requests before returning, but is allowed to be called
>>>>> from interrupt context where sleeping to wait for completion is not
>>>>> possible.
>>>>>
>>>>> As temporary workarounds for this framework limitation:
>>>>>
>>>>> 1. In __dwc3_gadget_ep_enable(), add a check for the
>>>>> DWC3_EP_TRANSFER_STARTED flag before issuing a new StartTransfer.
>>>>> This prevents a second StartTransfer on an already busy endpoint,
>>>>> eliminating the resource conflict.
>>>>>
>>>>> 2. In __dwc3_gadget_ep_disable(), preserve the DWC3_EP_TRANSFER_STARTED
>>>>> flag when masking dep->flags if it is actually set, preventing the
>>>>> race with dwc3_gadget_ep_queue() from corrupting the flag state.
>>>>>
>>>>> These changes eliminate the "No resource for ep" warnings and potential
>>>>> kernel panics caused by panic_on_warn.
>>>>>
>>>>> dwc3 13200000.dwc3: No resource for ep1out
>>>>> WARNING: CPU: 0 PID: 700 at drivers/usb/dwc3/gadget.c:398 dwc3_send_gadget_ep_cmd+0x2f8/0x76c
>>>>> Call trace:
>>>>> dwc3_send_gadget_ep_cmd+0x2f8/0x76c
>>>>> __dwc3_gadget_ep_enable+0x490/0x7c0
>>>>> dwc3_gadget_ep_enable+0x6c/0xe4
>>>>> usb_ep_enable+0x5c/0x15c
>>>>> mp_eth_stop+0xd4/0x11c
>>>>> __dev_close_many+0x160/0x1c8
>>>>> __dev_change_flags+0xfc/0x220
>>>>> dev_change_flags+0x24/0x70
>>>>> devinet_ioctl+0x434/0x524
>>>>> inet_ioctl+0xa8/0x224
>>>>> sock_do_ioctl+0x74/0x128
>>>>> sock_ioctl+0x3bc/0x468
>>>>> __arm64_sys_ioctl+0xa8/0xe4
>>>>> invoke_syscall+0x58/0x10c
>>>>> el0_svc_common+0xa8/0xdc
>>>>> do_el0_svc+0x1c/0x28
>>>>> el0_svc+0x38/0x88
>>>>> el0t_64_sync_handler+0x70/0xbc
>>>>> el0t_64_sync+0x1a8/0x1ac
>>>>>
>>>>> Cc: stable@vger.kernel.org
>>>>> Signed-off-by: Selvarasu Ganesan <selvarasu.g@samsung.com>
>>>>> ---
>>>>>
>>>>> Note: No Fixes tag is added because this is a workaround for the
>>>>> gadget framework issue where the gadget framework calls usb_ep_disable()
>>>>> in interrupt context without ensuring endpoint flushing completes.
>>>>> A proper fix requires refactoring the framework to make sure
>>>>> usb_ep_disable is invoked in process context.
>>>>>
>>>>> Changes in v3:
>>>>> - Revised the commit message to detail the real gadget framework issue
>>>>> pointed out by the reviewer.
>>>>> - Merged the two fixes for the same ep wringing into one patch.
>>>>> Link to v2: https://protect2.fireeye.com/v1/url?k=5535d0f4-344e7a7d-55345bbb-74fe48600034-c617d7b77912682d&q=1&e=2a6508e6-6363-4d6e-b6ab-ce6c77832c8d&u=https%3A%2F%2Flore.kernel.org%2Flinux-usb%2F20251117155920.643-1-selvarasu.g%40samsung.com%2F
>>>>>
>>>>> Changes in v2:
>>>>> - Removed change-id.
>>>>> - Updated commit message.
>>>>> Link to v1: https://protect2.fireeye.com/v1/url?k=c8daed0d-a9a14784-c8db6642-74fe48600034-8488506d5854e40d&q=1&e=2a6508e6-6363-4d6e-b6ab-ce6c77832c8d&u=https%3A%2F%2Flore.kernel.org%2Flinux-usb%2F20251117152812.622-1-selvarasu.g%40samsung.com%2F
>>>>> ---
>>>>> drivers/usb/dwc3/gadget.c | 22 ++++++++++++++++++++--
>>>>> 1 file changed, 20 insertions(+), 2 deletions(-)
>>>>>
>>>>> diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
>>>>> index 0a688904ce8c5..3af1bbfe3d92b 100644
>>>>> --- a/drivers/usb/dwc3/gadget.c
>>>>> +++ b/drivers/usb/dwc3/gadget.c
>>>>> @@ -971,8 +971,9 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
>>>>> * Issue StartTransfer here with no-op TRB so we can always rely on No
>>>>> * Response Update Transfer command.
>>>>> */
>>>>> - if (usb_endpoint_xfer_bulk(desc) ||
>>>>> - usb_endpoint_xfer_int(desc)) {
>>>>> + if ((usb_endpoint_xfer_bulk(desc) ||
>>>>> + usb_endpoint_xfer_int(desc)) &&
>>>>> + !(dep->flags & DWC3_EP_TRANSFER_STARTED)) {
>>>>> struct dwc3_gadget_ep_cmd_params params;
>>>>> struct dwc3_trb *trb;
>>>>> dma_addr_t trb_dma;
>>>>> @@ -1096,6 +1097,23 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
>>>>> */
>>>>> if (dep->flags & DWC3_EP_DELAY_STOP)
>>>>> mask |= (DWC3_EP_DELAY_STOP | DWC3_EP_TRANSFER_STARTED);
>>>>> +
>>>>> + /*
>>>>> + * When dwc3_gadget_ep_disable() calls dwc3_gadget_giveback(),
>>>>> + * the dwc->lock is temporarily released. If dwc3_gadget_ep_queue()
>>>>> + * runs in that window it may set the DWC3_EP_TRANSFER_STARTED flag as
>>>>> + * part of dwc3_send_gadget_ep_cmd. The original code cleared the flag
>>>>> + * unconditionally in the mask operation, which could overwrite the
>>>>> + * concurrent modification.
>>>>> + *
>>>>> + * As a workaround for the interrupt context constraint where we cannot
>>>>> + * wait for endpoint flushing, preserve the DWC3_EP_TRANSFER_STARTED
>>>>> + * flag if it is set, avoiding resource conflicts until the framework
>>>>> + * is fixed to properly synchronize endpoint lifecycle management.
>>>>> + */
>>>>> + if (dep->flags & DWC3_EP_TRANSFER_STARTED)
>>>>> + mask |= DWC3_EP_TRANSFER_STARTED;
>>>>> +
>>>>> dep->flags &= mask;
>>>>>
>>>>> /* Clear out the ep descriptors for non-ep0 */
>>>>> --
>>>>> 2.34.1
>>>>>
>>>> Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
>>>>
>>> Oh wait, don't pick this patch up yet.
>>>
>>> This will cause a regression for UAS device. When switching alt-setting
>>> interface for BOT to UASP, the device needs to issue a Start Transfer
>>> command.
>>>
>>> This workaround won't work. Can we fix the usb_ep_disable() interface
>>> and rework this instead?
>>>
>>> BR,
>>> Thinh
>> Hi Thinh,
>>
>> We’re trying to see how this change could cause a regression for UAS
>> devices.
>> Could you explain why the workaround might be a problem for UAS? Are you
>> concerned that it could miss a valid StartTransfer when a previous
>> transfer finishes later than expected as part of ep_disable?
> In UAS, the device controller uses the first PRIME to synchronize with
> the host to determine whether the Start Transfer command can initiate
> the stream. After configuring an endpoint, if we issue the StartTransfer
> command too late, then the device controller may not initiate the
> transfer (sending ERDY), and host will not know when to start the
> transfer.
HI Thinh,
Sorry for the delayed response. We're seeing this issue a lot in Exynos
platform, so we want to get it fixed.
Thanks for the explanation about UASP in last comment.
We agree that for UASP, the new StartTransfer sent in ep_enable() is
what makes the controller send ERDY for the host's first PRIME. The old
patch skipped that StartTransfer that was wrong. If it is skipped and no
request is queued right away, the controller never sends ERDY for the
first PRIME and the UAS device possible hangs as you mentioned.
The new patch is simpler, we won't skip it and just trigger it once the
inflight end transfer is done.
Please see the proposed sequence,
1. usb_ep_disable() --> EndTransfer deferred due to pending control
transfer data/status stages (DWC3_EP_DELAY_STOP), old transfer still
active in HW.
2. usb_ep_enable() on the same endpoint --> instead of issuing the
new StartTransfer, set DWC3_EP_PENDING_START_TRANSFER (New flag) as below,
/* __dwc3_gadget_ep_enable() */
if (dep->flags & (DWC3_EP_DELAY_STOP |
DWC3_EP_END_TRANSFER_PENDING |
DWC3_EP_TRANSFER_STARTED))
dep->flags |= DWC3_EP_PENDING_START_TRANSFER;
else
ret = dwc3_gadget_ep_start_noop_transfer(dep); /* unchanged path */
3. Control request finishes --> next SETUP --> existing delayed stop
retry in dwc3_ep0_out_start() sends the EndTransfer for all delayed stop
endpoints.
4. EndTransfer completion and trigger a previously skipped
StartTransfer in ep_enable by checking DWC3_EP_PENDING_START_TRANSFER.
/* dwc3_gadget_endpoint_command_complete() */
if (dep->flags & DWC3_EP_PENDING_START_TRANSFER) {
dep->flags &= ~DWC3_EP_PENDING_START_TRANSFER;
dwc3_gadget_ep_start_noop_transfer(dep);
}
So the endpoint gets the same start/stop sequence you described, only
sent later, it goes out as soon as the EndTransfer finishes. So this is
high possible of happens before the host's first PRIME arrives. ERDY is
still sent, no UAS regression.
The below testing log is for the reference. You can see the timestamp
where pending start transfer triggered.
[ 3273.597476] Entry __dwc3_gadget_ep_disable
[ 3273.597481] dwc3_remove_requests ep1out skip stop transfer due to
DWC3_EP_DELAY_STOP
[ 3273.597510] Entry __dwc3_gadget_ep_enable 1045 dep->name =ep1out
dep->flags =e009
[ 3273.597590] dwc3_gadget_endpoint_command_complete 3857 dep->name
=ep1out dep->flags =4009 --> Triggered skipped Start transfer when
endpoint command completion is done.
Thanks,
Selva
> So we have this workaround that we would have the device Start and Stop
> the endpoint immediately just to arm the endpoint for UASP transfers. In
> the newer IPs, this workaround may not be needed.
>
>> If we don’t use this temporary fix, the driver can still report “EP
>> resource busy” when an earlier StartTransfer hasn’t finished
>> before ep_disable returns. That can happen when a UAS device needs to
>> start a new transfer during ep_enable while the prior transfer is still
>> pending.
>>
>> The patch simply blocks a second StartTransfer when the same endpoint
>> already has a transfer in progress to prevent a “EP resource busy” issue.
>>
>> And it can cause a new StartTransfer to be issued later from ep_queue
>> while the starttransfer that should have been started during ep_enable
>> is skipped.
>>
> Also, if the gadget driver just uses usb_ep_disable() to handle the
> teardown instead of proactively dequeuing all the active requests, then
> the DWC3_EP_TRANSFER_STARTED flag will still be cleared immediately on
> usb_ep_disable() and we will still run into this issue again.
>
> Another workaround is to have the dwc3 driver retry the command after a
> small delay if there's no resource error report. Only do dev_WARN after
> a few times of the same failure.
>
> Ideally, we should fix the usb_ep_disable() and have the composite
> framework properly handle the "wait" for completion.
>
> BR,
> Thinh
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3] usb: dwc3: gadget: Prevent EP resource conflicts during StartTransfer
2026-09-24 12:05 ` Selvarasu Ganesan
@ 2026-09-24 12:24 ` Selvarasu Ganesan
2026-09-29 3:08 ` Thinh Nguyen
1 sibling, 0 replies; 9+ messages in thread
From: Selvarasu Ganesan @ 2026-09-24 12:24 UTC (permalink / raw)
To: Thinh Nguyen
Cc: gregkh, linux-usb, linux-kernel, jh0801.jung, dh10.jung,
akash.m5, hongpooh.kim, eomji.oh, h10.kim, shijie.cai,
alim.akhtar, muhammed.ali, thiagu.r, pritam.sutar, stable
On 9/24/2026 5:35 PM, Selvarasu Ganesan wrote:
>
> On 3/7/2026 3:11 AM, Thinh Nguyen wrote:
>> On Fri, Mar 06, 2026, Selvarasu Ganesan wrote:
>>> On 3/3/2026 6:09 AM, Thinh Nguyen wrote:
>>>> On Sat, Feb 28, 2026, Thinh Nguyen wrote:
>>>>> On Fri, Feb 27, 2026, Selvarasu Ganesan wrote:
>>>>>> The below “No resource for ep” warning appears when a StartTransfer
>>>>>> command is issued for bulk or interrupt endpoints in
>>>>>> `dwc3_gadget_ep_enable` while a previous StartTransfer on the same
>>>>>> endpoint is still in progress. The gadget functions drivers can
>>>>>> invoke
>>>>>> `usb_ep_enable` (which triggers a new StartTransfer command)
>>>>>> before the
>>>>>> earlier transfer has completed. Because the previous
>>>>>> StartTransfer is
>>>>>> still active, `dwc3_gadget_ep_disable` can skip the required
>>>>>> `EndTransfer` due to `DWC3_EP_DELAY_STOP`, leading to the endpoint
>>>>>> resources are busy for previous StartTransfer and warning ("No
>>>>>> resource
>>>>>> for ep") from dwc3 driver.
>>>>>>
>>>>>> Additionally, a race condition exists between
>>>>>> dwc3_gadget_ep_disable()
>>>>>> and dwc3_gadget_ep_queue() when manipulating dep->flags. When
>>>>>> dwc3_gadget_ep_disable() calls dwc3_gadget_giveback(), the
>>>>>> dwc->lock is
>>>>>> temporarily released. If dwc3_gadget_ep_queue() runs in that
>>>>>> window, it
>>>>>> may set the DWC3_EP_TRANSFER_STARTED flag as part of
>>>>>> dwc3_send_gadget_ep_cmd(). When ep_disable resumes, it
>>>>>> unconditionally
>>>>>> clears all flags except those explicitly masked, potentially
>>>>>> clearing
>>>>>> DWC3_EP_TRANSFER_STARTED even though a new transfer has started.
>>>>>> This
>>>>>> leads to "No resource for ep" warnings on subsequent StartTransfer
>>>>>> attempts.
>>>>>>
>>>>>> The underlying framework issue is that usb_ep_disable() is
>>>>>> expected to
>>>>>> complete pending requests before returning, but is allowed to be
>>>>>> called
>>>>>> from interrupt context where sleeping to wait for completion is not
>>>>>> possible.
>>>>>>
>>>>>> As temporary workarounds for this framework limitation:
>>>>>>
>>>>>> 1. In __dwc3_gadget_ep_enable(), add a check for the
>>>>>> DWC3_EP_TRANSFER_STARTED flag before issuing a new
>>>>>> StartTransfer.
>>>>>> This prevents a second StartTransfer on an already busy
>>>>>> endpoint,
>>>>>> eliminating the resource conflict.
>>>>>>
>>>>>> 2. In __dwc3_gadget_ep_disable(), preserve the
>>>>>> DWC3_EP_TRANSFER_STARTED
>>>>>> flag when masking dep->flags if it is actually set,
>>>>>> preventing the
>>>>>> race with dwc3_gadget_ep_queue() from corrupting the flag
>>>>>> state.
>>>>>>
>>>>>> These changes eliminate the "No resource for ep" warnings and
>>>>>> potential
>>>>>> kernel panics caused by panic_on_warn.
>>>>>>
>>>>>> dwc3 13200000.dwc3: No resource for ep1out
>>>>>> WARNING: CPU: 0 PID: 700 at drivers/usb/dwc3/gadget.c:398
>>>>>> dwc3_send_gadget_ep_cmd+0x2f8/0x76c
>>>>>> Call trace:
>>>>>> dwc3_send_gadget_ep_cmd+0x2f8/0x76c
>>>>>> __dwc3_gadget_ep_enable+0x490/0x7c0
>>>>>> dwc3_gadget_ep_enable+0x6c/0xe4
>>>>>> usb_ep_enable+0x5c/0x15c
>>>>>> mp_eth_stop+0xd4/0x11c
>>>>>> __dev_close_many+0x160/0x1c8
>>>>>> __dev_change_flags+0xfc/0x220
>>>>>> dev_change_flags+0x24/0x70
>>>>>> devinet_ioctl+0x434/0x524
>>>>>> inet_ioctl+0xa8/0x224
>>>>>> sock_do_ioctl+0x74/0x128
>>>>>> sock_ioctl+0x3bc/0x468
>>>>>> __arm64_sys_ioctl+0xa8/0xe4
>>>>>> invoke_syscall+0x58/0x10c
>>>>>> el0_svc_common+0xa8/0xdc
>>>>>> do_el0_svc+0x1c/0x28
>>>>>> el0_svc+0x38/0x88
>>>>>> el0t_64_sync_handler+0x70/0xbc
>>>>>> el0t_64_sync+0x1a8/0x1ac
>>>>>>
>>>>>> Cc: stable@vger.kernel.org
>>>>>> Signed-off-by: Selvarasu Ganesan <selvarasu.g@samsung.com>
>>>>>> ---
>>>>>>
>>>>>> Note: No Fixes tag is added because this is a workaround for the
>>>>>> gadget framework issue where the gadget framework calls
>>>>>> usb_ep_disable()
>>>>>> in interrupt context without ensuring endpoint flushing completes.
>>>>>> A proper fix requires refactoring the framework to make sure
>>>>>> usb_ep_disable is invoked in process context.
>>>>>>
>>>>>> Changes in v3:
>>>>>> - Revised the commit message to detail the real gadget
>>>>>> framework issue
>>>>>> pointed out by the reviewer.
>>>>>> - Merged the two fixes for the same ep wringing into one patch.
>>>>>> Link to v2:
>>>>>> https://protect2.fireeye.com/v1/url?k=5535d0f4-344e7a7d-55345bbb-74fe48600034-c617d7b77912682d&q=1&e=2a6508e6-6363-4d6e-b6ab-ce6c77832c8d&u=https%3A%2F%2Flore.kernel.org%2Flinux-usb%2F20251117155920.643-1-selvarasu.g%40samsung.com%2F
>>>>>>
>>>>>> Changes in v2:
>>>>>> - Removed change-id.
>>>>>> - Updated commit message.
>>>>>> Link to v1:
>>>>>> https://protect2.fireeye.com/v1/url?k=c8daed0d-a9a14784-c8db6642-74fe48600034-8488506d5854e40d&q=1&e=2a6508e6-6363-4d6e-b6ab-ce6c77832c8d&u=https%3A%2F%2Flore.kernel.org%2Flinux-usb%2F20251117152812.622-1-selvarasu.g%40samsung.com%2F
>>>>>> ---
>>>>>> drivers/usb/dwc3/gadget.c | 22 ++++++++++++++++++++--
>>>>>> 1 file changed, 20 insertions(+), 2 deletions(-)
>>>>>>
>>>>>> diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
>>>>>> index 0a688904ce8c5..3af1bbfe3d92b 100644
>>>>>> --- a/drivers/usb/dwc3/gadget.c
>>>>>> +++ b/drivers/usb/dwc3/gadget.c
>>>>>> @@ -971,8 +971,9 @@ static int __dwc3_gadget_ep_enable(struct
>>>>>> dwc3_ep *dep, unsigned int action)
>>>>>> * Issue StartTransfer here with no-op TRB so we can
>>>>>> always rely on No
>>>>>> * Response Update Transfer command.
>>>>>> */
>>>>>> - if (usb_endpoint_xfer_bulk(desc) ||
>>>>>> - usb_endpoint_xfer_int(desc)) {
>>>>>> + if ((usb_endpoint_xfer_bulk(desc) ||
>>>>>> + usb_endpoint_xfer_int(desc)) &&
>>>>>> + !(dep->flags & DWC3_EP_TRANSFER_STARTED)) {
>>>>>> struct dwc3_gadget_ep_cmd_params params;
>>>>>> struct dwc3_trb *trb;
>>>>>> dma_addr_t trb_dma;
>>>>>> @@ -1096,6 +1097,23 @@ static int __dwc3_gadget_ep_disable(struct
>>>>>> dwc3_ep *dep)
>>>>>> */
>>>>>> if (dep->flags & DWC3_EP_DELAY_STOP)
>>>>>> mask |= (DWC3_EP_DELAY_STOP | DWC3_EP_TRANSFER_STARTED);
>>>>>> +
>>>>>> + /*
>>>>>> + * When dwc3_gadget_ep_disable() calls dwc3_gadget_giveback(),
>>>>>> + * the dwc->lock is temporarily released. If
>>>>>> dwc3_gadget_ep_queue()
>>>>>> + * runs in that window it may set the
>>>>>> DWC3_EP_TRANSFER_STARTED flag as
>>>>>> + * part of dwc3_send_gadget_ep_cmd. The original code
>>>>>> cleared the flag
>>>>>> + * unconditionally in the mask operation, which could
>>>>>> overwrite the
>>>>>> + * concurrent modification.
>>>>>> + *
>>>>>> + * As a workaround for the interrupt context constraint
>>>>>> where we cannot
>>>>>> + * wait for endpoint flushing, preserve the
>>>>>> DWC3_EP_TRANSFER_STARTED
>>>>>> + * flag if it is set, avoiding resource conflicts until the
>>>>>> framework
>>>>>> + * is fixed to properly synchronize endpoint lifecycle
>>>>>> management.
>>>>>> + */
>>>>>> + if (dep->flags & DWC3_EP_TRANSFER_STARTED)
>>>>>> + mask |= DWC3_EP_TRANSFER_STARTED;
>>>>>> +
>>>>>> dep->flags &= mask;
>>>>>> /* Clear out the ep descriptors for non-ep0 */
>>>>>> --
>>>>>> 2.34.1
>>>>>>
>>>>> Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
>>>>>
>>>> Oh wait, don't pick this patch up yet.
>>>>
>>>> This will cause a regression for UAS device. When switching
>>>> alt-setting
>>>> interface for BOT to UASP, the device needs to issue a Start Transfer
>>>> command.
>>>>
>>>> This workaround won't work. Can we fix the usb_ep_disable() interface
>>>> and rework this instead?
>>>>
>>>> BR,
>>>> Thinh
>>> Hi Thinh,
>>>
>>> We’re trying to see how this change could cause a regression for UAS
>>> devices.
>>> Could you explain why the workaround might be a problem for UAS? Are
>>> you
>>> concerned that it could miss a valid StartTransfer when a previous
>>> transfer finishes later than expected as part of ep_disable?
>> In UAS, the device controller uses the first PRIME to synchronize with
>> the host to determine whether the Start Transfer command can initiate
>> the stream. After configuring an endpoint, if we issue the StartTransfer
>> command too late, then the device controller may not initiate the
>> transfer (sending ERDY), and host will not know when to start the
>> transfer.
>
>
> HI Thinh,
>
> Sorry for the delayed response. We're seeing this issue a lot in
> Exynos platform, so we want to get it fixed.
>
> Thanks for the explanation about UASP in last comment.
>
> We agree that for UASP, the new StartTransfer sent in ep_enable() is
> what makes the controller send ERDY for the host's first PRIME. The
> old patch skipped that StartTransfer that was wrong. If it is skipped
> and no request is queued right away, the controller never sends ERDY
> for the first PRIME and the UAS device possible hangs as you mentioned.
>
> The new patch is simpler, we won't skip it and just trigger it once
> the inflight end transfer is done.
>
> Please see the proposed sequence,
>
> 1. usb_ep_disable() --> EndTransfer deferred due to pending control
> transfer data/status stages (DWC3_EP_DELAY_STOP), old transfer still
> active in HW.
> 2. usb_ep_enable() on the same endpoint --> instead of issuing the
> new StartTransfer, set DWC3_EP_PENDING_START_TRANSFER (New flag) as
> below,
>
> /* __dwc3_gadget_ep_enable() */
> if (dep->flags & (DWC3_EP_DELAY_STOP |
> DWC3_EP_END_TRANSFER_PENDING |
> DWC3_EP_TRANSFER_STARTED))
> dep->flags |= DWC3_EP_PENDING_START_TRANSFER;
> else
> ret = dwc3_gadget_ep_start_noop_transfer(dep); /* unchanged
> path */
>
> 3. Control request finishes --> next SETUP --> existing delayed stop
> retry in dwc3_ep0_out_start() sends the EndTransfer for all delayed
> stop endpoints.
> 4. EndTransfer completion and trigger a previously skipped
> StartTransfer in ep_enable by checking DWC3_EP_PENDING_START_TRANSFER.
>
> /* dwc3_gadget_endpoint_command_complete() */
> if (dep->flags & DWC3_EP_PENDING_START_TRANSFER) {
> dep->flags &= ~DWC3_EP_PENDING_START_TRANSFER;
> dwc3_gadget_ep_start_noop_transfer(dep);
> }
>
> So the endpoint gets the same start/stop sequence you described, only
> sent later, it goes out as soon as the EndTransfer finishes. So this
> is high possible of happens before the host's first PRIME arrives.
> ERDY is still sent, no UAS regression.
>
> The below testing log is for the reference. You can see the timestamp
> where pending start transfer triggered.
>
> [ 3273.597476] Entry __dwc3_gadget_ep_disable
> [ 3273.597481] dwc3_remove_requests ep1out skip stop transfer due to
> DWC3_EP_DELAY_STOP
> [ 3273.597510] Entry __dwc3_gadget_ep_enable 1045 dep->name =ep1out
> dep->flags =e009
> [ 3273.597590] dwc3_gadget_endpoint_command_complete 3857 dep->name
> =ep1out dep->flags =4009 --> Triggered skipped Start transfer when
> endpoint command completion is done.
>
>
> Thanks,
> Selva
>
The below patch is for this proposed sequence,
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index fa0f16ffafef..40fe930a6c2c 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -906,6 +906,72 @@ static int dwc3_gadget_resize_tx_fifos(struct
dwc3_ep *dep)
return 0;
}
+/**
+ * dwc3_gadget_ep_start_noop_transfer - start a no-op transfer on the
endpoint
+ * @dep: bulk or interrupt endpoint
+ *
+ * Issue StartTransfer here with no-op TRB so we can always rely on No
+ * Response Update Transfer command.
+ *
+ * Caller should take care of locking. The endpoint must be enabled and
must
+ * not have an active transfer.
+ */
+static int dwc3_gadget_ep_start_noop_transfer(struct dwc3_ep *dep)
+{
+ struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3_trb *trb;
+ struct dwc3 *dwc = dep->dwc;
+ dma_addr_t trb_dma;
+ u32 cmd;
+ int ret;
+
+ memset(¶ms, 0, sizeof(params));
+ trb = &dep->trb_pool[0];
+ trb_dma = dwc3_trb_dma_offset(dep, trb);
+
+ params.param0 = upper_32_bits(trb_dma);
+ params.param1 = lower_32_bits(trb_dma);
+
+ cmd = DWC3_DEPCMD_STARTTRANSFER;
+
+ ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
+ if (ret < 0)
+ return ret;
+
+ if (dep->stream_capable) {
+ /*
+ * For streams, at start, there maybe a race where the
+ * host primes the endpoint before the function driver
+ * queues a request to initiate a stream. In that case,
+ * the controller will not see the prime to generate the
+ * ERDY and start stream. To workaround this, issue a
+ * no-op TRB as normal, but end it immediately. As a
+ * result, when the function driver queues the request,
+ * the next START_TRANSFER command will cause the
+ * controller to generate an ERDY to initiate the
+ * stream.
+ */
+ dwc3_stop_active_transfer(dep, true, true);
+
+ /*
+ * All stream eps will reinitiate stream on NoStream
+ * rejection until we can determine that the host can
+ * prime after the first transfer.
+ *
+ * However, if the controller is capable of
+ * TXF_FLUSH_BYPASS, then IN direction endpoints will
+ * automatically restart the stream without the driver
+ * initiation.
+ */
+ if (!dep->direction ||
+ !(dwc->hwparams.hwparams9 &
+ DWC3_GHWPARAMS9_DEV_TXF_FLUSH_BYPASS))
+ dep->flags |= DWC3_EP_FORCE_RESTART_STREAM;
+ }
+
+ return 0;
+}
+
/**
* __dwc3_gadget_ep_enable - initializes a hw endpoint
* @dep: endpoint to be initialized
@@ -973,52 +1039,26 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep
*dep, unsigned int action)
*/
if (usb_endpoint_xfer_bulk(desc) ||
usb_endpoint_xfer_int(desc)) {
- struct dwc3_gadget_ep_cmd_params params;
- struct dwc3_trb *trb;
- dma_addr_t trb_dma;
- u32 cmd;
-
- memset(¶ms, 0, sizeof(params));
- trb = &dep->trb_pool[0];
- trb_dma = dwc3_trb_dma_offset(dep, trb);
-
- params.param0 = upper_32_bits(trb_dma);
- params.param1 = lower_32_bits(trb_dma);
-
- cmd = DWC3_DEPCMD_STARTTRANSFER;
-
- ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
- if (ret < 0)
- return ret;
-
- if (dep->stream_capable) {
+ if (dep->flags & (DWC3_EP_DELAY_STOP |
+ DWC3_EP_END_TRANSFER_PENDING |
+ DWC3_EP_TRANSFER_STARTED)) {
/*
- * For streams, at start, there maybe a race
where the
- * host primes the endpoint before the function
driver
- * queues a request to initiate a stream. In
that case,
- * the controller will not see the prime to
generate the
- * ERDY and start stream. To workaround this,
issue a
- * no-op TRB as normal, but end it immediately. As a
- * result, when the function driver queues the
request,
- * the next START_TRANSFER command will cause the
- * controller to generate an ERDY to initiate the
- * stream.
- */
- dwc3_stop_active_transfer(dep, true, true);
-
- /*
- * All stream eps will reinitiate stream on NoStream
- * rejection.
- *
- * However, if the controller is capable of
- * TXF_FLUSH_BYPASS, then IN direction endpoints
will
- * automatically restart the stream without the
driver
- * initiation.
+ * The previous transfer has not been ended in
+ * hardware yet: the prior usb_ep_disable() left a
+ * deferred (DWC3_EP_DELAY_STOP) or in-flight
+ * (DWC3_EP_END_TRANSFER_PENDING) EndTransfer
+ * command. Issuing StartTransfer now would be
+ * rejected with "No resource" because the endpoint
+ * still has an active transfer. Defer the no-op
+ * StartTransfer; it will be replayed from
+ * dwc3_gadget_endpoint_command_complete() once the
+ * EndTransfer has completed.
*/
- if (!dep->direction ||
- !(dwc->hwparams.hwparams9 &
- DWC3_GHWPARAMS9_DEV_TXF_FLUSH_BYPASS))
- dep->flags |= DWC3_EP_FORCE_RESTART_STREAM;
+ dep->flags |= DWC3_EP_PENDING_START_TRANSFER;
+ } else {
+ ret = dwc3_gadget_ep_start_noop_transfer(dep);
+ if (ret < 0)
+ return ret;
}
}
@@ -1096,6 +1136,23 @@ static int __dwc3_gadget_ep_disable(struct
dwc3_ep *dep)
*/
if (dep->flags & DWC3_EP_DELAY_STOP)
mask |= (DWC3_EP_DELAY_STOP | DWC3_EP_TRANSFER_STARTED);
+
+ /*
+ * When dwc3_gadget_ep_disable() calls dwc3_gadget_giveback(),
+ * the dwc->lock is temporarily released. If dwc3_gadget_ep_queue()
+ * runs in that window it may set the DWC3_EP_TRANSFER_STARTED
flag as
+ * part of dwc3_send_gadget_ep_cmd. The original code cleared
the flag
+ * unconditionally in the mask operation, which could overwrite the
+ * concurrent modification.
+ *
+ * As a workaround for the interrupt context constraint where we
cannot
+ * wait for endpoint flushing, preserve the DWC3_EP_TRANSFER_STARTED
+ * flag if it is set, avoiding resource conflicts until the
framework
+ * is fixed to properly synchronize endpoint lifecycle management.
+ */
+ if (dep->flags & DWC3_EP_TRANSFER_STARTED)
+ mask |= DWC3_EP_TRANSFER_STARTED;
+
dep->flags &= mask;
/* Clear out the ep descriptors for non-ep0 */
@@ -3861,6 +3918,22 @@ static void
dwc3_gadget_endpoint_command_complete(struct dwc3_ep *dep,
dwc3_ep0_send_delayed_status(dwc);
}
+ /*
+ * __dwc3_gadget_ep_enable() deferred the no-op StartTransfer while
+ * this EndTransfer was pending (the endpoint was re-enabled before
+ * the deferred EndTransfer of a prior usb_ep_disable() completed).
+ * Now that the previous transfer has been ended in hardware, issue
+ * the no-op StartTransfer to (re)arm the endpoint, mirroring the
+ * original usb_ep_enable() path. If requests were queued in the
+ * meantime, the delayed-start kick below issues an UpdateTransfer
+ * with the first request's TRB, exactly like the normal
+ * enable-then-queue sequence.
+ */
+ if (dep->flags & DWC3_EP_PENDING_START_TRANSFER) {
+ dep->flags &= ~DWC3_EP_PENDING_START_TRANSFER;
+ dwc3_gadget_ep_start_noop_transfer(dep);
+ }
+
if ((dep->flags & DWC3_EP_DELAY_START) &&
!usb_endpoint_xfer_isoc(dep->endpoint.desc))
__dwc3_gadget_kick_transfer(dep);
Thanks,
Selva
>> So we have this workaround that we would have the device Start and Stop
>> the endpoint immediately just to arm the endpoint for UASP transfers. In
>> the newer IPs, this workaround may not be needed.
>>
>>> If we don’t use this temporary fix, the driver can still report “EP
>>> resource busy” when an earlier StartTransfer hasn’t finished
>>> before ep_disable returns. That can happen when a UAS device needs to
>>> start a new transfer during ep_enable while the prior transfer is still
>>> pending.
>>>
>>> The patch simply blocks a second StartTransfer when the same endpoint
>>> already has a transfer in progress to prevent a “EP resource busy”
>>> issue.
>>>
>>> And it can cause a new StartTransfer to be issued later from ep_queue
>>> while the starttransfer that should have been started during ep_enable
>>> is skipped.
>>>
>> Also, if the gadget driver just uses usb_ep_disable() to handle the
>> teardown instead of proactively dequeuing all the active requests, then
>> the DWC3_EP_TRANSFER_STARTED flag will still be cleared immediately on
>> usb_ep_disable() and we will still run into this issue again.
>>
>> Another workaround is to have the dwc3 driver retry the command after a
>> small delay if there's no resource error report. Only do dev_WARN after
>> a few times of the same failure.
>>
>> Ideally, we should fix the usb_ep_disable() and have the composite
>> framework properly handle the "wait" for completion.
>>
>> BR,
>> Thinh
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3] usb: dwc3: gadget: Prevent EP resource conflicts during StartTransfer
2026-09-24 12:05 ` Selvarasu Ganesan
2026-09-24 12:24 ` Selvarasu Ganesan
@ 2026-09-29 3:08 ` Thinh Nguyen
2026-09-29 7:04 ` Selvarasu Ganesan
1 sibling, 1 reply; 9+ messages in thread
From: Thinh Nguyen @ 2026-09-29 3:08 UTC (permalink / raw)
To: Selvarasu Ganesan
Cc: Thinh Nguyen, gregkh, linux-usb, linux-kernel, jh0801.jung,
dh10.jung, akash.m5, hongpooh.kim, eomji.oh, h10.kim, shijie.cai,
alim.akhtar, muhammed.ali, thiagu.r, pritam.sutar, stable
On Thu, Sep 24, 2026, Selvarasu Ganesan wrote:
>
> On 3/7/2026 3:11 AM, Thinh Nguyen wrote:
> > On Fri, Mar 06, 2026, Selvarasu Ganesan wrote:
> >> On 3/3/2026 6:09 AM, Thinh Nguyen wrote:
> >>> On Sat, Feb 28, 2026, Thinh Nguyen wrote:
> >>>> On Fri, Feb 27, 2026, Selvarasu Ganesan wrote:
> >>>>> The below “No resource for ep” warning appears when a StartTransfer
> >>>>> command is issued for bulk or interrupt endpoints in
> >>>>> `dwc3_gadget_ep_enable` while a previous StartTransfer on the same
> >>>>> endpoint is still in progress. The gadget functions drivers can invoke
> >>>>> `usb_ep_enable` (which triggers a new StartTransfer command) before the
> >>>>> earlier transfer has completed. Because the previous StartTransfer is
> >>>>> still active, `dwc3_gadget_ep_disable` can skip the required
> >>>>> `EndTransfer` due to `DWC3_EP_DELAY_STOP`, leading to the endpoint
> >>>>> resources are busy for previous StartTransfer and warning ("No resource
> >>>>> for ep") from dwc3 driver.
> >>>>>
> >>>>> Additionally, a race condition exists between dwc3_gadget_ep_disable()
> >>>>> and dwc3_gadget_ep_queue() when manipulating dep->flags. When
> >>>>> dwc3_gadget_ep_disable() calls dwc3_gadget_giveback(), the dwc->lock is
> >>>>> temporarily released. If dwc3_gadget_ep_queue() runs in that window, it
> >>>>> may set the DWC3_EP_TRANSFER_STARTED flag as part of
> >>>>> dwc3_send_gadget_ep_cmd(). When ep_disable resumes, it unconditionally
> >>>>> clears all flags except those explicitly masked, potentially clearing
> >>>>> DWC3_EP_TRANSFER_STARTED even though a new transfer has started. This
> >>>>> leads to "No resource for ep" warnings on subsequent StartTransfer
> >>>>> attempts.
> >>>>>
> >>>>> The underlying framework issue is that usb_ep_disable() is expected to
> >>>>> complete pending requests before returning, but is allowed to be called
> >>>>> from interrupt context where sleeping to wait for completion is not
> >>>>> possible.
> >>>>>
> >>>>> As temporary workarounds for this framework limitation:
> >>>>>
> >>>>> 1. In __dwc3_gadget_ep_enable(), add a check for the
> >>>>> DWC3_EP_TRANSFER_STARTED flag before issuing a new StartTransfer.
> >>>>> This prevents a second StartTransfer on an already busy endpoint,
> >>>>> eliminating the resource conflict.
> >>>>>
> >>>>> 2. In __dwc3_gadget_ep_disable(), preserve the DWC3_EP_TRANSFER_STARTED
> >>>>> flag when masking dep->flags if it is actually set, preventing the
> >>>>> race with dwc3_gadget_ep_queue() from corrupting the flag state.
> >>>>>
> >>>>> These changes eliminate the "No resource for ep" warnings and potential
> >>>>> kernel panics caused by panic_on_warn.
> >>>>>
> >>>>> dwc3 13200000.dwc3: No resource for ep1out
> >>>>> WARNING: CPU: 0 PID: 700 at drivers/usb/dwc3/gadget.c:398 dwc3_send_gadget_ep_cmd+0x2f8/0x76c
> >>>>> Call trace:
> >>>>> dwc3_send_gadget_ep_cmd+0x2f8/0x76c
> >>>>> __dwc3_gadget_ep_enable+0x490/0x7c0
> >>>>> dwc3_gadget_ep_enable+0x6c/0xe4
> >>>>> usb_ep_enable+0x5c/0x15c
> >>>>> mp_eth_stop+0xd4/0x11c
> >>>>> __dev_close_many+0x160/0x1c8
> >>>>> __dev_change_flags+0xfc/0x220
> >>>>> dev_change_flags+0x24/0x70
> >>>>> devinet_ioctl+0x434/0x524
> >>>>> inet_ioctl+0xa8/0x224
> >>>>> sock_do_ioctl+0x74/0x128
> >>>>> sock_ioctl+0x3bc/0x468
> >>>>> __arm64_sys_ioctl+0xa8/0xe4
> >>>>> invoke_syscall+0x58/0x10c
> >>>>> el0_svc_common+0xa8/0xdc
> >>>>> do_el0_svc+0x1c/0x28
> >>>>> el0_svc+0x38/0x88
> >>>>> el0t_64_sync_handler+0x70/0xbc
> >>>>> el0t_64_sync+0x1a8/0x1ac
> >>>>>
> >>>>> Cc: stable@vger.kernel.org
> >>>>> Signed-off-by: Selvarasu Ganesan <selvarasu.g@samsung.com>
> >>>>> ---
> >>>>>
> >>>>> Note: No Fixes tag is added because this is a workaround for the
> >>>>> gadget framework issue where the gadget framework calls usb_ep_disable()
> >>>>> in interrupt context without ensuring endpoint flushing completes.
> >>>>> A proper fix requires refactoring the framework to make sure
> >>>>> usb_ep_disable is invoked in process context.
> >>>>>
> >>>>> Changes in v3:
> >>>>> - Revised the commit message to detail the real gadget framework issue
> >>>>> pointed out by the reviewer.
> >>>>> - Merged the two fixes for the same ep wringing into one patch.
> >>>>> Link to v2: https://urldefense.com/v3/__https://protect2.fireeye.com/v1/url?k=5535d0f4-344e7a7d-55345bbb-74fe48600034-c617d7b77912682d&q=1&e=2a6508e6-6363-4d6e-b6ab-ce6c77832c8d&u=https*3A*2F*2Flore.kernel.org*2Flinux-usb*2F20251117155920.643-1-selvarasu.g*40samsung.com*2F__;JSUlJSUlJQ!!A4F2R9G_pg!dR0yufG2VvgMnkC0ZJ4eK3I5v7C5q2ry8MYAYSNdWhMNM2zPkO35fGBJJHPS-GhG4y9hzmrCcO16V1ASehHBYxvxQWQ$
> >>>>>
> >>>>> Changes in v2:
> >>>>> - Removed change-id.
> >>>>> - Updated commit message.
> >>>>> Link to v1: https://urldefense.com/v3/__https://protect2.fireeye.com/v1/url?k=c8daed0d-a9a14784-c8db6642-74fe48600034-8488506d5854e40d&q=1&e=2a6508e6-6363-4d6e-b6ab-ce6c77832c8d&u=https*3A*2F*2Flore.kernel.org*2Flinux-usb*2F20251117152812.622-1-selvarasu.g*40samsung.com*2F__;JSUlJSUlJQ!!A4F2R9G_pg!dR0yufG2VvgMnkC0ZJ4eK3I5v7C5q2ry8MYAYSNdWhMNM2zPkO35fGBJJHPS-GhG4y9hzmrCcO16V1ASehHB1Y4qk_k$
> >>>>> ---
> >>>>> drivers/usb/dwc3/gadget.c | 22 ++++++++++++++++++++--
> >>>>> 1 file changed, 20 insertions(+), 2 deletions(-)
> >>>>>
> >>>>> diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
> >>>>> index 0a688904ce8c5..3af1bbfe3d92b 100644
> >>>>> --- a/drivers/usb/dwc3/gadget.c
> >>>>> +++ b/drivers/usb/dwc3/gadget.c
> >>>>> @@ -971,8 +971,9 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
> >>>>> * Issue StartTransfer here with no-op TRB so we can always rely on No
> >>>>> * Response Update Transfer command.
> >>>>> */
> >>>>> - if (usb_endpoint_xfer_bulk(desc) ||
> >>>>> - usb_endpoint_xfer_int(desc)) {
> >>>>> + if ((usb_endpoint_xfer_bulk(desc) ||
> >>>>> + usb_endpoint_xfer_int(desc)) &&
> >>>>> + !(dep->flags & DWC3_EP_TRANSFER_STARTED)) {
> >>>>> struct dwc3_gadget_ep_cmd_params params;
> >>>>> struct dwc3_trb *trb;
> >>>>> dma_addr_t trb_dma;
> >>>>> @@ -1096,6 +1097,23 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
> >>>>> */
> >>>>> if (dep->flags & DWC3_EP_DELAY_STOP)
> >>>>> mask |= (DWC3_EP_DELAY_STOP | DWC3_EP_TRANSFER_STARTED);
> >>>>> +
> >>>>> + /*
> >>>>> + * When dwc3_gadget_ep_disable() calls dwc3_gadget_giveback(),
> >>>>> + * the dwc->lock is temporarily released. If dwc3_gadget_ep_queue()
> >>>>> + * runs in that window it may set the DWC3_EP_TRANSFER_STARTED flag as
> >>>>> + * part of dwc3_send_gadget_ep_cmd. The original code cleared the flag
> >>>>> + * unconditionally in the mask operation, which could overwrite the
> >>>>> + * concurrent modification.
> >>>>> + *
> >>>>> + * As a workaround for the interrupt context constraint where we cannot
> >>>>> + * wait for endpoint flushing, preserve the DWC3_EP_TRANSFER_STARTED
> >>>>> + * flag if it is set, avoiding resource conflicts until the framework
> >>>>> + * is fixed to properly synchronize endpoint lifecycle management.
> >>>>> + */
> >>>>> + if (dep->flags & DWC3_EP_TRANSFER_STARTED)
> >>>>> + mask |= DWC3_EP_TRANSFER_STARTED;
> >>>>> +
> >>>>> dep->flags &= mask;
> >>>>>
> >>>>> /* Clear out the ep descriptors for non-ep0 */
> >>>>> --
> >>>>> 2.34.1
> >>>>>
> >>>> Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
> >>>>
> >>> Oh wait, don't pick this patch up yet.
> >>>
> >>> This will cause a regression for UAS device. When switching alt-setting
> >>> interface for BOT to UASP, the device needs to issue a Start Transfer
> >>> command.
> >>>
> >>> This workaround won't work. Can we fix the usb_ep_disable() interface
> >>> and rework this instead?
> >>>
> >>> BR,
> >>> Thinh
> >> Hi Thinh,
> >>
> >> We’re trying to see how this change could cause a regression for UAS
> >> devices.
> >> Could you explain why the workaround might be a problem for UAS? Are you
> >> concerned that it could miss a valid StartTransfer when a previous
> >> transfer finishes later than expected as part of ep_disable?
> > In UAS, the device controller uses the first PRIME to synchronize with
> > the host to determine whether the Start Transfer command can initiate
> > the stream. After configuring an endpoint, if we issue the StartTransfer
> > command too late, then the device controller may not initiate the
> > transfer (sending ERDY), and host will not know when to start the
> > transfer.
>
>
> HI Thinh,
>
> Sorry for the delayed response. We're seeing this issue a lot in Exynos
> platform, so we want to get it fixed.
>
> Thanks for the explanation about UASP in last comment.
>
> We agree that for UASP, the new StartTransfer sent in ep_enable() is
> what makes the controller send ERDY for the host's first PRIME. The old
> patch skipped that StartTransfer that was wrong. If it is skipped and no
> request is queued right away, the controller never sends ERDY for the
> first PRIME and the UAS device possible hangs as you mentioned.
>
> The new patch is simpler, we won't skip it and just trigger it once the
> inflight end transfer is done.
>
> Please see the proposed sequence,
>
> 1. usb_ep_disable() --> EndTransfer deferred due to pending control
> transfer data/status stages (DWC3_EP_DELAY_STOP), old transfer still
> active in HW.
> 2. usb_ep_enable() on the same endpoint --> instead of issuing the
> new StartTransfer, set DWC3_EP_PENDING_START_TRANSFER (New flag) as below,
>
> /* __dwc3_gadget_ep_enable() */
> if (dep->flags & (DWC3_EP_DELAY_STOP |
> DWC3_EP_END_TRANSFER_PENDING |
> DWC3_EP_TRANSFER_STARTED))
> dep->flags |= DWC3_EP_PENDING_START_TRANSFER;
> else
> ret = dwc3_gadget_ep_start_noop_transfer(dep); /* unchanged path */
>
> 3. Control request finishes --> next SETUP --> existing delayed stop
> retry in dwc3_ep0_out_start() sends the EndTransfer for all delayed stop
> endpoints.
> 4. EndTransfer completion and trigger a previously skipped
> StartTransfer in ep_enable by checking DWC3_EP_PENDING_START_TRANSFER.
>
> /* dwc3_gadget_endpoint_command_complete() */
> if (dep->flags & DWC3_EP_PENDING_START_TRANSFER) {
> dep->flags &= ~DWC3_EP_PENDING_START_TRANSFER;
> dwc3_gadget_ep_start_noop_transfer(dep);
> }
>
> So the endpoint gets the same start/stop sequence you described, only
> sent later, it goes out as soon as the EndTransfer finishes. So this is
> high possible of happens before the host's first PRIME arrives. ERDY is
> still sent, no UAS regression.
>
> The below testing log is for the reference. You can see the timestamp
> where pending start transfer triggered.
>
> [ 3273.597476] Entry __dwc3_gadget_ep_disable
> [ 3273.597481] dwc3_remove_requests ep1out skip stop transfer due to
> DWC3_EP_DELAY_STOP
> [ 3273.597510] Entry __dwc3_gadget_ep_enable 1045 dep->name =ep1out
> dep->flags =e009
> [ 3273.597590] dwc3_gadget_endpoint_command_complete 3857 dep->name
> =ep1out dep->flags =4009 --> Triggered skipped Start transfer when
> endpoint command completion is done.
>
Hi Selvarasu,
I just wanted to point out that dwc3_gadget_ep_start_noop_transfer()
should only be needed for stream endpoints. I'll take a closer look at
the rest of the changes later this week.
Regarding the original comment about avoiding the wait for command
completion due to the No Response Update Transfer command does not
provide any meaningful performance benefit since it only applies to the
initial Start Transfer command.
Thanks,
Thinh
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3] usb: dwc3: gadget: Prevent EP resource conflicts during StartTransfer
2026-09-29 3:08 ` Thinh Nguyen
@ 2026-09-29 7:04 ` Selvarasu Ganesan
0 siblings, 0 replies; 9+ messages in thread
From: Selvarasu Ganesan @ 2026-09-29 7:04 UTC (permalink / raw)
To: Thinh Nguyen
Cc: gregkh, linux-usb, linux-kernel, jh0801.jung, dh10.jung,
akash.m5, hongpooh.kim, eomji.oh, h10.kim, shijie.cai,
alim.akhtar, muhammed.ali, thiagu.r, pritam.sutar, stable
On 9/29/2026 8:38 AM, Thinh Nguyen wrote:
> On Thu, Sep 24, 2026, Selvarasu Ganesan wrote:
>> On 3/7/2026 3:11 AM, Thinh Nguyen wrote:
>>> On Fri, Mar 06, 2026, Selvarasu Ganesan wrote:
>>>> On 3/3/2026 6:09 AM, Thinh Nguyen wrote:
>>>>> On Sat, Feb 28, 2026, Thinh Nguyen wrote:
>>>>>> On Fri, Feb 27, 2026, Selvarasu Ganesan wrote:
>>>>>>> The below “No resource for ep” warning appears when a StartTransfer
>>>>>>> command is issued for bulk or interrupt endpoints in
>>>>>>> `dwc3_gadget_ep_enable` while a previous StartTransfer on the same
>>>>>>> endpoint is still in progress. The gadget functions drivers can invoke
>>>>>>> `usb_ep_enable` (which triggers a new StartTransfer command) before the
>>>>>>> earlier transfer has completed. Because the previous StartTransfer is
>>>>>>> still active, `dwc3_gadget_ep_disable` can skip the required
>>>>>>> `EndTransfer` due to `DWC3_EP_DELAY_STOP`, leading to the endpoint
>>>>>>> resources are busy for previous StartTransfer and warning ("No resource
>>>>>>> for ep") from dwc3 driver.
>>>>>>>
>>>>>>> Additionally, a race condition exists between dwc3_gadget_ep_disable()
>>>>>>> and dwc3_gadget_ep_queue() when manipulating dep->flags. When
>>>>>>> dwc3_gadget_ep_disable() calls dwc3_gadget_giveback(), the dwc->lock is
>>>>>>> temporarily released. If dwc3_gadget_ep_queue() runs in that window, it
>>>>>>> may set the DWC3_EP_TRANSFER_STARTED flag as part of
>>>>>>> dwc3_send_gadget_ep_cmd(). When ep_disable resumes, it unconditionally
>>>>>>> clears all flags except those explicitly masked, potentially clearing
>>>>>>> DWC3_EP_TRANSFER_STARTED even though a new transfer has started. This
>>>>>>> leads to "No resource for ep" warnings on subsequent StartTransfer
>>>>>>> attempts.
>>>>>>>
>>>>>>> The underlying framework issue is that usb_ep_disable() is expected to
>>>>>>> complete pending requests before returning, but is allowed to be called
>>>>>>> from interrupt context where sleeping to wait for completion is not
>>>>>>> possible.
>>>>>>>
>>>>>>> As temporary workarounds for this framework limitation:
>>>>>>>
>>>>>>> 1. In __dwc3_gadget_ep_enable(), add a check for the
>>>>>>> DWC3_EP_TRANSFER_STARTED flag before issuing a new StartTransfer.
>>>>>>> This prevents a second StartTransfer on an already busy endpoint,
>>>>>>> eliminating the resource conflict.
>>>>>>>
>>>>>>> 2. In __dwc3_gadget_ep_disable(), preserve the DWC3_EP_TRANSFER_STARTED
>>>>>>> flag when masking dep->flags if it is actually set, preventing the
>>>>>>> race with dwc3_gadget_ep_queue() from corrupting the flag state.
>>>>>>>
>>>>>>> These changes eliminate the "No resource for ep" warnings and potential
>>>>>>> kernel panics caused by panic_on_warn.
>>>>>>>
>>>>>>> dwc3 13200000.dwc3: No resource for ep1out
>>>>>>> WARNING: CPU: 0 PID: 700 at drivers/usb/dwc3/gadget.c:398 dwc3_send_gadget_ep_cmd+0x2f8/0x76c
>>>>>>> Call trace:
>>>>>>> dwc3_send_gadget_ep_cmd+0x2f8/0x76c
>>>>>>> __dwc3_gadget_ep_enable+0x490/0x7c0
>>>>>>> dwc3_gadget_ep_enable+0x6c/0xe4
>>>>>>> usb_ep_enable+0x5c/0x15c
>>>>>>> mp_eth_stop+0xd4/0x11c
>>>>>>> __dev_close_many+0x160/0x1c8
>>>>>>> __dev_change_flags+0xfc/0x220
>>>>>>> dev_change_flags+0x24/0x70
>>>>>>> devinet_ioctl+0x434/0x524
>>>>>>> inet_ioctl+0xa8/0x224
>>>>>>> sock_do_ioctl+0x74/0x128
>>>>>>> sock_ioctl+0x3bc/0x468
>>>>>>> __arm64_sys_ioctl+0xa8/0xe4
>>>>>>> invoke_syscall+0x58/0x10c
>>>>>>> el0_svc_common+0xa8/0xdc
>>>>>>> do_el0_svc+0x1c/0x28
>>>>>>> el0_svc+0x38/0x88
>>>>>>> el0t_64_sync_handler+0x70/0xbc
>>>>>>> el0t_64_sync+0x1a8/0x1ac
>>>>>>>
>>>>>>> Cc: stable@vger.kernel.org
>>>>>>> Signed-off-by: Selvarasu Ganesan <selvarasu.g@samsung.com>
>>>>>>> ---
>>>>>>>
>>>>>>> Note: No Fixes tag is added because this is a workaround for the
>>>>>>> gadget framework issue where the gadget framework calls usb_ep_disable()
>>>>>>> in interrupt context without ensuring endpoint flushing completes.
>>>>>>> A proper fix requires refactoring the framework to make sure
>>>>>>> usb_ep_disable is invoked in process context.
>>>>>>>
>>>>>>> Changes in v3:
>>>>>>> - Revised the commit message to detail the real gadget framework issue
>>>>>>> pointed out by the reviewer.
>>>>>>> - Merged the two fixes for the same ep wringing into one patch.
>>>>>>> Link to v2: https://protect2.fireeye.com/v1/url?k=dd1a51eb-82a25949-dd1bdaa4-000babff88b5-867f0f86789e003a&q=1&e=b9b4dadf-c903-42b6-bd70-3d09d4ad7c04&u=https%3A%2F%2Flore.kernel.org%2Flinux-usb%2F20251117155920.643-1-selvarasu.g%40samsung.com%2F
>>>>>>>
>>>>>>> Changes in v2:
>>>>>>> - Removed change-id.
>>>>>>> - Updated commit message.
>>>>>>> Link to v1: https://protect2.fireeye.com/v1/url?k=a7e00d4e-f85805ec-a7e18601-000babff88b5-fe0fb8c41d582e73&q=1&e=b9b4dadf-c903-42b6-bd70-3d09d4ad7c04&u=https%3A%2F%2Flore.kernel.org%2Flinux-usb%2F20251117152812.622-1-selvarasu.g%40samsung.com%2F
>>>>>>> ---
>>>>>>> drivers/usb/dwc3/gadget.c | 22 ++++++++++++++++++++--
>>>>>>> 1 file changed, 20 insertions(+), 2 deletions(-)
>>>>>>>
>>>>>>> diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
>>>>>>> index 0a688904ce8c5..3af1bbfe3d92b 100644
>>>>>>> --- a/drivers/usb/dwc3/gadget.c
>>>>>>> +++ b/drivers/usb/dwc3/gadget.c
>>>>>>> @@ -971,8 +971,9 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
>>>>>>> * Issue StartTransfer here with no-op TRB so we can always rely on No
>>>>>>> * Response Update Transfer command.
>>>>>>> */
>>>>>>> - if (usb_endpoint_xfer_bulk(desc) ||
>>>>>>> - usb_endpoint_xfer_int(desc)) {
>>>>>>> + if ((usb_endpoint_xfer_bulk(desc) ||
>>>>>>> + usb_endpoint_xfer_int(desc)) &&
>>>>>>> + !(dep->flags & DWC3_EP_TRANSFER_STARTED)) {
>>>>>>> struct dwc3_gadget_ep_cmd_params params;
>>>>>>> struct dwc3_trb *trb;
>>>>>>> dma_addr_t trb_dma;
>>>>>>> @@ -1096,6 +1097,23 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
>>>>>>> */
>>>>>>> if (dep->flags & DWC3_EP_DELAY_STOP)
>>>>>>> mask |= (DWC3_EP_DELAY_STOP | DWC3_EP_TRANSFER_STARTED);
>>>>>>> +
>>>>>>> + /*
>>>>>>> + * When dwc3_gadget_ep_disable() calls dwc3_gadget_giveback(),
>>>>>>> + * the dwc->lock is temporarily released. If dwc3_gadget_ep_queue()
>>>>>>> + * runs in that window it may set the DWC3_EP_TRANSFER_STARTED flag as
>>>>>>> + * part of dwc3_send_gadget_ep_cmd. The original code cleared the flag
>>>>>>> + * unconditionally in the mask operation, which could overwrite the
>>>>>>> + * concurrent modification.
>>>>>>> + *
>>>>>>> + * As a workaround for the interrupt context constraint where we cannot
>>>>>>> + * wait for endpoint flushing, preserve the DWC3_EP_TRANSFER_STARTED
>>>>>>> + * flag if it is set, avoiding resource conflicts until the framework
>>>>>>> + * is fixed to properly synchronize endpoint lifecycle management.
>>>>>>> + */
>>>>>>> + if (dep->flags & DWC3_EP_TRANSFER_STARTED)
>>>>>>> + mask |= DWC3_EP_TRANSFER_STARTED;
>>>>>>> +
>>>>>>> dep->flags &= mask;
>>>>>>>
>>>>>>> /* Clear out the ep descriptors for non-ep0 */
>>>>>>> --
>>>>>>> 2.34.1
>>>>>>>
>>>>>> Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
>>>>>>
>>>>> Oh wait, don't pick this patch up yet.
>>>>>
>>>>> This will cause a regression for UAS device. When switching alt-setting
>>>>> interface for BOT to UASP, the device needs to issue a Start Transfer
>>>>> command.
>>>>>
>>>>> This workaround won't work. Can we fix the usb_ep_disable() interface
>>>>> and rework this instead?
>>>>>
>>>>> BR,
>>>>> Thinh
>>>> Hi Thinh,
>>>>
>>>> We’re trying to see how this change could cause a regression for UAS
>>>> devices.
>>>> Could you explain why the workaround might be a problem for UAS? Are you
>>>> concerned that it could miss a valid StartTransfer when a previous
>>>> transfer finishes later than expected as part of ep_disable?
>>> In UAS, the device controller uses the first PRIME to synchronize with
>>> the host to determine whether the Start Transfer command can initiate
>>> the stream. After configuring an endpoint, if we issue the StartTransfer
>>> command too late, then the device controller may not initiate the
>>> transfer (sending ERDY), and host will not know when to start the
>>> transfer.
>>
>> HI Thinh,
>>
>> Sorry for the delayed response. We're seeing this issue a lot in Exynos
>> platform, so we want to get it fixed.
>>
>> Thanks for the explanation about UASP in last comment.
>>
>> We agree that for UASP, the new StartTransfer sent in ep_enable() is
>> what makes the controller send ERDY for the host's first PRIME. The old
>> patch skipped that StartTransfer that was wrong. If it is skipped and no
>> request is queued right away, the controller never sends ERDY for the
>> first PRIME and the UAS device possible hangs as you mentioned.
>>
>> The new patch is simpler, we won't skip it and just trigger it once the
>> inflight end transfer is done.
>>
>> Please see the proposed sequence,
>>
>> 1. usb_ep_disable() --> EndTransfer deferred due to pending control
>> transfer data/status stages (DWC3_EP_DELAY_STOP), old transfer still
>> active in HW.
>> 2. usb_ep_enable() on the same endpoint --> instead of issuing the
>> new StartTransfer, set DWC3_EP_PENDING_START_TRANSFER (New flag) as below,
>>
>> /* __dwc3_gadget_ep_enable() */
>> if (dep->flags & (DWC3_EP_DELAY_STOP |
>> DWC3_EP_END_TRANSFER_PENDING |
>> DWC3_EP_TRANSFER_STARTED))
>> dep->flags |= DWC3_EP_PENDING_START_TRANSFER;
>> else
>> ret = dwc3_gadget_ep_start_noop_transfer(dep); /* unchanged path */
>>
>> 3. Control request finishes --> next SETUP --> existing delayed stop
>> retry in dwc3_ep0_out_start() sends the EndTransfer for all delayed stop
>> endpoints.
>> 4. EndTransfer completion and trigger a previously skipped
>> StartTransfer in ep_enable by checking DWC3_EP_PENDING_START_TRANSFER.
>>
>> /* dwc3_gadget_endpoint_command_complete() */
>> if (dep->flags & DWC3_EP_PENDING_START_TRANSFER) {
>> dep->flags &= ~DWC3_EP_PENDING_START_TRANSFER;
>> dwc3_gadget_ep_start_noop_transfer(dep);
>> }
>>
>> So the endpoint gets the same start/stop sequence you described, only
>> sent later, it goes out as soon as the EndTransfer finishes. So this is
>> high possible of happens before the host's first PRIME arrives. ERDY is
>> still sent, no UAS regression.
>>
>> The below testing log is for the reference. You can see the timestamp
>> where pending start transfer triggered.
>>
>> [ 3273.597476] Entry __dwc3_gadget_ep_disable
>> [ 3273.597481] dwc3_remove_requests ep1out skip stop transfer due to
>> DWC3_EP_DELAY_STOP
>> [ 3273.597510] Entry __dwc3_gadget_ep_enable 1045 dep->name =ep1out
>> dep->flags =e009
>> [ 3273.597590] dwc3_gadget_endpoint_command_complete 3857 dep->name
>> =ep1out dep->flags =4009 --> Triggered skipped Start transfer when
>> endpoint command completion is done.
>>
> Hi Selvarasu,
>
> I just wanted to point out that dwc3_gadget_ep_start_noop_transfer()
> should only be needed for stream endpoints. I'll take a closer look at
> the rest of the changes later this week.
Hi Thinh,
Thanks for you feedback.
Agreed, The dwc3_gadget_ep_start_noop_transfer() should only be needed
for stream endpoints.
Thanks,
Selva
>
> Regarding the original comment about avoiding the wait for command
> completion due to the No Response Update Transfer command does not
> provide any meaningful performance benefit since it only applies to the
> initial Start Transfer command.
>
> Thanks,
> Thinh
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-09-29 7:04 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <CGME20260227121338epcas5p4baebb406db37f07223545b2f85751bf2@epcas5p4.samsung.com>
2026-02-27 12:12 ` [PATCH v3] usb: dwc3: gadget: Prevent EP resource conflicts during StartTransfer Selvarasu Ganesan
2026-02-28 0:27 ` Thinh Nguyen
2026-03-03 0:39 ` Thinh Nguyen
2026-03-06 13:06 ` Selvarasu Ganesan
2026-03-06 21:41 ` Thinh Nguyen
2026-09-24 12:05 ` Selvarasu Ganesan
2026-09-24 12:24 ` Selvarasu Ganesan
2026-09-29 3:08 ` Thinh Nguyen
2026-09-29 7:04 ` Selvarasu Ganesan
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®