Every teardown of a gadget that includes a FunctionFS function logs an error:
[ 188.808339] dwc3 23000000.usb: request 00000000ad92f1c4 was not queued to ep0out
Reproduced on Flipper One (rk3576, USB-C gadget port usb@23000000) with the
ncmmscmtp composite (ffs.mtp + mass_storage.0 + ncm.usb0), kernel 7.2.0-rc3.
It also appears at boot when the gadget setup retries, in which case it is followed by
failed to start ncmmscmtp: -19 / couldn't find an available UDC or it's busy.
Cause
Two correct-in-isolation behaviours collide:
functionfs_unbind() dequeues ep0req unconditionally before freeing it. That is
deliberate: usb_ep_free_request() requires the request no longer be queued, which
commit ce405d5 ("usb: gadget: f_fs: Ensure ep0req is dequeued before
free_request", 2022-12-15) made explicit to close a use-after-free.
- By that point the request has long completed, so
dwc3_gadget_ep_dequeue() finds it on
none of its lists (cancelled_list, pending_list, started_list) and falls through to
the dev_err() at drivers/usb/dwc3/gadget.c:2184, returning -EINVAL.
Impact
Cosmetic. Both callers ignore the return value and free the request immediately; nothing
leaks and no transfer is affected. It is noise in every gadget teardown, which makes real
USB errors harder to spot in logs.
Evidence
ftrace stack on dwc3_gadget_ep_dequeue, triggered by stopping the composite
(usb-gadget-setup.sh stop, i.e. a write to the gadget's UDC attribute):
>> [ 188.808339] dwc3 23000000.usb: request 00000000ad92f1c4 was not queued to ep0out
usb-gadget-setu-7074 [005] 188.808328: dwc3_gadget_ep_dequeue <-usb_ep_dequeue
usb-gadget-setu-7074 [005] 188.808333: <stack trace>
=> dwc3_gadget_ep_dequeue
=> usb_ep_dequeue
=> functionfs_unbind
=> ffs_func_unbind
=> purge_configs_funcs
=> configfs_composite_unbind
=> gadget_unbind_driver
=> device_remove
=> device_release_driver_internal
=> driver_detach
=> bus_remove_driver
=> gadget_dev_desc_UDC_store
=> configfs_write_iter
=> vfs_write
=> ksys_write
=> __arm64_sys_write
=> invoke_syscall
=> el0_svc_common.constprop.0
=> do_el0_svc
=> el0_svc
=> el0t_64_sync_handler
=> el0t_64_sync
Upstream state: unfixed, and previously contested
-
The same class of bug was fixed in the UDC driver for cdnsp: commit 34f08eb
("usb: cdnsp: Fixes issue with dequeuing not queued requests", 2023-07-13, cc stable)
added three lines, if (request->status != -EINPROGRESS) return 0;, for a gadget
dequeuing a not-queued request during module unload.
-
A dwc3-side fix was posted and stalled:
usb: dwc3: ep0: Add implementation of ep0_dequeue separately
(Nov 2022), still state=new. Maintainer Thinh Nguyen questioned the premise:
Just curious, how do you hit/test this scenario? For other endpoint types, I can see
possible scenarios where a dequeue may be needed, but I don't see one for control
transfer. [...] So, where does ep0_dequeue come into play here?
and asked for the opposite of a silent no-op: "Need to check if the control transfer is
active and the input request is the valid request to dequeue. Return error code if it's
not."
That thread is answerable now: the f_fs commit that creates this caller landed after that
discussion, so the stack trace above is exactly the caller he asked for.
Possible fixes
- Mirror cdnsp in dwc3 - return 0 early when
request->status != -EINPROGRESS. dwc3
sets -EINPROGRESS on queue for both normal endpoints (gadget.c:2002) and ep0
(ep0.c:95), and overwrites it on completion, so the check is reliable. Downside: it is
the silent no-op the dwc3 maintainer explicitly did not want, so it would likely be
rejected upstream as-is.
- Follow the maintainer's preference - validate that the control transfer is active and
return an error otherwise, reviving the 2022 thread with this reproducer attached.
- Carry a local patch only to silence the log, and leave upstream alone.
Preference welcome before anything is sent to linux-usb.
Every teardown of a gadget that includes a FunctionFS function logs an error:
Reproduced on Flipper One (rk3576, USB-C gadget port
usb@23000000) with thencmmscmtpcomposite (ffs.mtp+mass_storage.0+ncm.usb0), kernel 7.2.0-rc3.It also appears at boot when the gadget setup retries, in which case it is followed by
failed to start ncmmscmtp: -19/couldn't find an available UDC or it's busy.Cause
Two correct-in-isolation behaviours collide:
functionfs_unbind()dequeuesep0requnconditionally before freeing it. That isdeliberate:
usb_ep_free_request()requires the request no longer be queued, whichcommit ce405d5 ("usb: gadget: f_fs: Ensure ep0req is dequeued before
free_request", 2022-12-15) made explicit to close a use-after-free.
dwc3_gadget_ep_dequeue()finds it onnone of its lists (
cancelled_list,pending_list,started_list) and falls through tothe
dev_err()atdrivers/usb/dwc3/gadget.c:2184, returning-EINVAL.Impact
Cosmetic. Both callers ignore the return value and free the request immediately; nothing
leaks and no transfer is affected. It is noise in every gadget teardown, which makes real
USB errors harder to spot in logs.
Evidence
ftrace stack on
dwc3_gadget_ep_dequeue, triggered by stopping the composite(
usb-gadget-setup.sh stop, i.e. a write to the gadget'sUDCattribute):Upstream state: unfixed, and previously contested
The same class of bug was fixed in the UDC driver for cdnsp: commit 34f08eb
("usb: cdnsp: Fixes issue with dequeuing not queued requests", 2023-07-13, cc stable)
added three lines,
if (request->status != -EINPROGRESS) return 0;, for a gadgetdequeuing a not-queued request during module unload.
A dwc3-side fix was posted and stalled:
usb: dwc3: ep0: Add implementation of ep0_dequeue separately
(Nov 2022), still
state=new. Maintainer Thinh Nguyen questioned the premise:and asked for the opposite of a silent no-op: "Need to check if the control transfer is
active and the input request is the valid request to dequeue. Return error code if it's
not."
That thread is answerable now: the f_fs commit that creates this caller landed after that
discussion, so the stack trace above is exactly the caller he asked for.
Possible fixes
request->status != -EINPROGRESS. dwc3sets
-EINPROGRESSon queue for both normal endpoints (gadget.c:2002) and ep0(
ep0.c:95), and overwrites it on completion, so the check is reliable. Downside: it isthe silent no-op the dwc3 maintainer explicitly did not want, so it would likely be
rejected upstream as-is.
return an error otherwise, reviving the 2022 thread with this reproducer attached.
Preference welcome before anything is sent to linux-usb.