usb: musb: gadget: don't flush a waiting RX packet
Subsystems (2)
v4 cover usb: musb: gadget: don't flush a waiting RX packet
Since commit 3fc137386c46 ("usb: musb: Fix hardware lockup on first Rx endpoint request"), musb_ep_restart() flushes the RX FIFO instead of calling rxstate().
When using some functions with musb, for example, f_mass_storage or f_acm, this code path will be used mid-communication, and it will cause data loss.
Patch 2 fixes this in musb_ep_restart() and schedules a bh work item instead of flushing and losing the data in the RX FIFO.
Patch 1 modifies the semantics of musb_ep.busy from a flag to a counter. If usb_ep_dequeue() runs at the same time as another musb_g_giveback(), busy could end up with a false 1; this would stall an endpoint. I added this patch in the series as prep for patch 2, because patch 2 would otherwise add one more case where such a race is possible -- a musb_g_giveback() of a work item with a musb_g_giveback() of nuke().
v3 -> v4:
- Move musb_gadget_enable_rx_kicks() in musb_resume() to be after musb_enable_interrupts() and under musb->lock (addresses sashiko's "premature and unlocked rx_kick queueing in musb_resume()")
v2 -> v3:
- Added patch 1: busy is a counter now (addresses sashiko's "overlapping musb_g_giveback() calls wedge ep->busy" and the nuke() part of "race and ordering bug in musb_gadget_disable() for rx_kick")
- Schedule a kick on every OUT ep when resuming to avoid stalls when the USB session is preserved across suspend/resume (addresses sashiko's "lost rx_kick across system suspend and resume")
v1 -> v2:
- musb_g_rx_kick() does not re-queue itself when busy is set. Instead, musb_g_giveback() re-queues it when rx_kick_deferred. This new flag is set in three cases:
1) busy is set inside musb_g_rx_kick()
2) musb_gadget_queue() adds an RX request to an empty request queue and busy is set
3) busy is set in the beginning of musb_g_rx() (addresses sashiko's "RX endpoint lockup after giveback in musb_g_rx_kick()", "softirq busy-loop and livelock in musb_g_rx_kick()", and "race and ordering bug in musb_gadget_disable() for rx_kick")
- musb_g_rx_kick checks musb->is_runtime_suspended now and exits early if the controller is suspended, and the work items are disabled during system suspend (addresses sashiko's
"unushed rx_kick work in musb_suspend() accesses disabled MMIO")
- This time tested also on a Pine A64+ (Allwinner A64), which has both MUSB and 4 cores.
v3: https://lore.kernel.org/linux-usb/[email protected]/
v2: https://lore.kernel.org/linux-usb/[email protected]/
v1: https://lore.kernel.org/linux-usb/[email protected]/
Elvira Khabirova (2):
usb: musb: gadget: count givebacks in busy
usb: musb: gadget: don't flush a waiting RX packet when queueing a
request
drivers/usb/musb/musb_core.c | 3 + drivers/usb/musb/musb_gadget.c | 128 ++++++++++++++++++++++++++++++--- drivers/usb/musb/musb_gadget.h | 11 ++- 3 files changed, 133 insertions(+), 9 deletions(-)
base-commit: c339f4010dea8b0d754f84ace70cd1b089878ef8
Signature · 1 line
2.53.0
PATCH v4 1/2 usb: musb: gadget: count givebacks in busy
The ep->busy flag is used by musb_g_giveback() to signal that req->complete() is running on an endpoint, the ep is busy and starting a new request is not appropriate right now.
It is only a flag and not a counter currently; musb_g_giveback() saves the old ep->busy value, sets busy to 1, releases musb->lock, runs req->complete(), restores the old ep->busy. This works when there is no possibility of two musb_g_giveback()s running in parallel.
This does not work when musb_gadget_dequeue()->...->musb_g_giveback() happens at the same time as musb_interrupt()->...->musb_g_giveback(). The above save-then-restore can get the busy flag stuck at 1.
CPU0 (interrupt) CPU1 (usb_ep_dequeue())
giveback: saved 0, busy = 1
unlock, ->complete()
giveback: saved 1, busy = 1 unlock, ->complete()
lock, busy = 0
lock, busy = 1
...and this gets the entire endpoint stuck.
This was reproduced and the patch was tested on a Pine A64+ (Allwinner A64, four cores, no DMA) with PREEMPT_DYNAMIC and lockdep, using a specially constructed f_fs function that keeps two AIO requests queued on a bulk endpoint and cancels the newer one with io_cancel(), in a loop. Tested with both a bulk IN and a bulk OUT endpoint.
If musb_g_giveback() were to be started from some other context, the same issue would come up.
"Promote" ep->busy from a flag to a counter.
musb_gadget_enable() should not reset busy to 0 anymore because musb_g_giveback() decrements busy, and if these two run in parallel, it would be problematic (cause an underflow).
--- drivers/usb/musb/musb_gadget.c | 10 +++++----- drivers/usb/musb/musb_gadget.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-)
drivers/usb/musb/musb_gadget.c +5 -5
@@ -129,7 +129,6 @@ __acquires(ep->musb->lock) {struct musb_request *req;struct musb *musb; -int busy = ep->busy; req =to_musb_request (request);
@@ -138,7 +137,7 @@ __acquires(ep->musb->lock) req->request.status = status; musb = req->musb; - ep->busy =1 ; + ep->busy++;spin_unlock (&musb->lock);if (!dma_mapping_error (&musb->g.dev, request->dma))
@@ -147,7 +146,7 @@ __acquires(ep->musb->lock)trace_musb_req_gb (req);usb_gadget_giveback_request (&req->ep->end_point, &req->request);spin_lock (&musb->lock); - ep->busy = busy; + ep->busy--; }/* ----------------------------------------------------------------------- */
@@ -162,7 +161,7 @@ static void nuke(struct musb_ep *ep, const int status)struct musb_request *req =NULL ;void __iomem *epio = ep->musb->endpoints[ep->current_epnum].regs; - ep->busy =1 ; + ep->busy++;if (is_dma_capable () && ep->dma) {struct dma_controller *c = ep->musb->dma_controller;
@@ -195,6 +194,8 @@ static void nuke(struct musb_ep *ep, const int status) req = list_first_entry(&ep->req_list, struct musb_request, list);musb_g_giveback (ep, &req->request, status); } + + ep->busy--; }/* ----------------------------------------------------------------------- */
@@ -1061,7 +1062,6 @@ static int musb_gadget_enable(struct usb_ep *ep, musb_ep->dma =NULL ; musb_ep->desc = desc; - musb_ep->busy =0 ; musb_ep->wedged =0 ; status =0 ;
drivers/usb/musb/musb_gadget.h +1 -1
@@ -90,7 +90,7 @@ struct musb_ep {u8 wedged; -/* true if lock must be dropped but req_list may not be advanced */ +/* number of givebacks in progress; req_list may not be advanced */ u8 busy;u8 hb_mult;
Signature · 1 line
2.53.0
[email protected]
AI reviewRe: [PATCH v4 1/2] usb: musb: gadget: count givebacks in busy
Thank you for your contribution! Sashiko AI review found 2 potential issues to consider: - [Medium] usb: musb: gadget: no TX restart when busy drops to 0 in musb_g_giveback() - [Low] usb: musb…
Thank you for your contribution! Sashiko AI review found 2 potential issues to consider:
- [Medium] usb: musb: gadget: no TX restart when busy drops to 0 in musb_g_giveback()
- [Low] usb: musb: gadget: missing Fixes trailer for ep->busy race fix --
commit 38e2611af12585f304377ba06a8095186ff11968
Author: Elvira Khabirova <[email protected]>
Quoted 35 lines
usb: musb: gadget: count givebacks in busy
The
ep->busyflag is used bymusb_g_giveback()to signal thatreq->complete()is running on an endpoint, the ep is busy and starting a new request is not appropriate right now.It is only a flag and not a counter currently;
musb_g_giveback()saves the oldep->busyvalue, sets busy to 1, releasesmusb->lock, runsreq->complete(), restores the oldep->busy. This works when there is no possibility of two musb_g_giveback()s running in parallel.This does not work when musb_gadget_dequeue()->...->musb_g_giveback() happens at the same time as musb_interrupt()->...->musb_g_giveback(). The above save-then-restore can get the busy flag stuck at 1.
CPU0 (interrupt) CPU1 (
usb_ep_dequeue())
giveback: saved 0, busy = 1
unlock, ->complete()
giveback: saved 1, busy = 1 unlock, ->complete()
lock, busy = 0
lock, busy = 1...and this gets the entire endpoint stuck.
This was reproduced and the patch was tested on a Pine A64+ (Allwinner A64, four cores, no DMA) with
PREEMPT_DYNAMICand lockdep, using a specially constructedf_fsfunction that keeps two AIO requests queued on a bulk endpoint and cancels the newer one withio_cancel(), in a loop. Tested with both a bulk IN and a bulk OUT endpoint.If
musb_g_giveback()were to be started from some other context, the same issue would come up.
Quoted 7 lines
"Promote"
ep->busyfrom a flag to a counter.
musb_gadget_enable()should not resetbusyto 0 anymore becausemusb_g_giveback()decrements busy, and if these two run in parallel, it would be problematic (cause an underflow).Signed-off-by Elvira Khabirova <[email protected]>
[Severity: Low]
Should this concurrency fix include a Fixes tag?
Quoted 22 lines
drivers/usb/musb/musb_gadget.c +1
-2@@ -129,7 +129,6 @@ __acquires(ep->musb->lock) {struct musb_request *req;struct musb *musb; -int busy = ep->busy; req =to_musb_request (request);@@ -138,7 +137,7 @@ __acquires(ep->musb->lock) req->request.status = status; musb = req->musb; - ep->busy =1 ; + ep->busy++;spin_unlock (&musb->lock);if (!dma_mapping_error (&musb->g.dev, request->dma))
drivers/usb/musb/musb_gadget.c __acquires(ep->musb->lock) · line 150
Quoted 6 lines
drivers/usb/musb/musb_gadget.c +1
-1@@ -147,7 +146,7 @@ __acquires(ep->musb->lock)trace_musb_req_gb (req);usb_gadget_giveback_request (&req->ep->end_point, &req->request);spin_lock (&musb->lock); - ep->busy = busy; + ep->busy--; }
[Severity: Medium]
Can an IN endpoint still stall when a new TX request is queued while a concurrent dequeue giveback holds ep->busy non-zero after musb_g_tx() has returned?
With two TX requests req1 and req2 queued on an IN endpoint:
CPU0 CPU1musb_g_tx()
musb_g_giveback(..., req1, 0)
list_del(&req1->list)
ep->busy++ (0 -> 1)
spin_unlock(&musb->lock)
req1->complete()
musb_gadget_dequeue(..., req2)
musb_g_giveback(..., req2, -ECONNRESET)
list_del(&req2->list)
ep->busy++ (1 -> 2)
spin_unlock(&musb->lock)
req2->complete()
spin_lock(&musb->lock)
ep->busy-- (2 -> 1)
next_request(musb_ep) == NULL
return
musb_gadget_queue(..., req3)
list_add_tail(&req3->list, ...)
!musb_ep->busy is false (busy == 1)
spin_lock(&musb->lock)
ep->busy-- (1 -> 0)
return <-- req3 is never startedWhen musb_g_tx() finishes its giveback first, req_list is empty because musb_gadget_dequeue() already unlinked req2, so musb_g_tx() exits while ep->busy is still 1. If req3 is queued before CPU1 finishes req2->complete(), musb_gadget_queue() skips starting I/O because musb_ep->busy is non-zero:
drivers/usb/musb/musb_gadget.c:musb_gadget_queue() {
...
/* it this is the head of the queue, start i/o ... */
if (!musb_ep->busy && &request->list == musb_ep->req_list.next) {
status = musb_queue_resume_work(musb,
musb_ep_restart_resume_work,
request);
...
}When CPU1 then decrements ep->busy to 0 and returns from musb_gadget_dequeue(), nothing calls txstate() for req3, so no further TX interrupt fires and the IN endpoint stays wedged with req3 at the head of req_list. Commit "usb: musb: gadget: don't flush a waiting RX packet when queueing a request" later in this series defers and kicks RX when ep->busy drops to 0, but TX requests (!request->tx is false) are not restarted there either.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
Re: [PATCH v4 1/2] usb: musb: gadget: count givebacks in busy
Quoted 51 lines · sashiko-bot
Thank you for your contribution! Sashiko AI review found 2 potential issues to consider:
- [Medium] usb: musb: gadget: no TX restart when busy drops to 0 inmusb_g_giveback()
- [Low] usb: musb: gadget: missing Fixes trailer forep->busyrace fix --commit
38e2611af12585f304377ba06a8095186ff11968
Author: Elvira Khabirova <[email protected]>Quoted 41 lines
usb: musb: gadget: count givebacks in busy
The
ep->busyflag is used bymusb_g_giveback()to signal thatreq->complete()is running on an endpoint, the ep is busy and starting a new request is not appropriate right now.It is only a flag and not a counter currently;
musb_g_giveback()saves the oldep->busyvalue, sets busy to 1, releasesmusb->lock, runsreq->complete(), restores the oldep->busy. This works when there is no possibility of two musb_g_giveback()s running in parallel.This does not work when musb_gadget_dequeue()->...->musb_g_giveback() happens at the same time as musb_interrupt()->...->musb_g_giveback(). The above save-then-restore can get the busy flag stuck at 1.
CPU0 (interrupt) CPU1 (
usb_ep_dequeue())
giveback: saved 0, busy = 1
unlock, ->complete()
giveback: saved 1, busy = 1 unlock, ->complete()
lock, busy = 0
lock, busy = 1...and this gets the entire endpoint stuck.
This was reproduced and the patch was tested on a Pine A64+ (Allwinner A64, four cores, no DMA) with
PREEMPT_DYNAMICand lockdep, using a specially constructedf_fsfunction that keeps two AIO requests queued on a bulk endpoint and cancels the newer one withio_cancel(), in a loop. Tested with both a bulk IN and a bulk OUT endpoint.If
musb_g_giveback()were to be started from some other context, the same issue would come up."Promote"
ep->busyfrom a flag to a counter.
musb_gadget_enable()should not resetbusyto 0 anymore becausemusb_g_giveback()decrements busy, and if these two run in parallel, it would be problematic (cause an underflow).
Quoted 6 lines
Quoted 1 line
Signed-off-by Elvira Khabirova <[email protected]>[Severity: Low]
Should this concurrency fix include a Fixes tag?Fixes 550a7375fe72 ("USB: Add MUSB and TUSB support")
Feels useless to mark this as "Fixes" for a commit from 2008 that brings MUSB support.
Quoted 76 lines
Quoted 22 lines
drivers/usb/musb/musb_gadget.c +1
-2@@ -129,7 +129,6 @@ __acquires(ep->musb->lock) {struct musb_request *req;struct musb *musb; -int busy = ep->busy; req =to_musb_request (request);@@ -138,7 +137,7 @@ __acquires(ep->musb->lock) req->request.status = status; musb = req->musb; - ep->busy =1 ; + ep->busy++;spin_unlock (&musb->lock);if (!dma_mapping_error (&musb->g.dev, request->dma))drivers/usb/musb/musb_gadget.c __acquires(ep->musb->lock) · line 150
Quoted 6 lines
drivers/usb/musb/musb_gadget.c +1
-1@@ -147,7 +146,7 @@ __acquires(ep->musb->lock)trace_musb_req_gb (req);usb_gadget_giveback_request (&req->ep->end_point, &req->request);spin_lock (&musb->lock); - ep->busy = busy; + ep->busy--; }[Severity: Medium]
Can an IN endpoint still stall when a new TX request is queued while a concurrent dequeue giveback holdsep->busynon-zero aftermusb_g_tx()has returned?With two TX requests req1 and req2 queued on an IN endpoint:
CPU0 CPU1
musb_g_tx()
musb_g_giveback(..., req1, 0)list_del(&req1->list) ep->busy++ (0 -> 1) spin_unlock(&musb->lock) req1->complete() musb_gadget_dequeue(..., req2) musb_g_giveback(..., req2, -ECONNRESET) list_del(&req2->list) ep->busy++ (1 -> 2) spin_unlock(&musb->lock) req2->complete() spin_lock(&musb->lock) ep->busy-- (2 -> 1) next_request(musb_ep) == NULL return musb_gadget_queue(..., req3) list_add_tail(&req3->list, ...) !musb_ep->busy is false (busy == 1) spin_lock(&musb->lock) ep->busy-- (1 -> 0) return <-- req3 is never startedWhen
musb_g_tx()finishes its giveback first,req_listis empty becausemusb_gadget_dequeue()already unlinked req2, somusb_g_tx()exits whileep->busyis still 1. If req3 is queued before CPU1 finishesreq2->complete(),musb_gadget_queue()skips starting I/O becausemusb_ep->busyis non-zero:drivers/usb/musb/musb_gadget.c:musb_gadget_queue() { ... /* it this is the head of the queue, start i/o ... */ if (!musb_ep->busy && &request->list == musb_ep->req_list.next) { status = musb_queue_resume_work(musb, musb_ep_restart_resume_work, request); ... }
Quoted 7 lines
When CPU1 then decrements
ep->busyto 0 and returns frommusb_gadget_dequeue(), nothing callstxstate()for req3, so no further TX interrupt fires and the IN endpoint stays wedged with req3 at the head ofreq_list. Commit "usb: musb: gadget: don't flush a waiting RX packet when queueing a request" later in this series defers and kicks RX whenep->busydrops to 0, but TX requests (!request->tx is false) are not restarted there either.
Yes, it can. This is pre-existing and not made worse by this patch. This needs a separate fix.
PATCH v4 2/2 usb: musb: gadget: don't flush a waiting RX packet when queueing a request
In reply to Elvira KhabirovaSince commit 3fc137386c46 ("usb: musb: Fix hardware lockup on first Rx endpoint request"), musb_ep_restart() flushes the RX FIFO instead of calling rxstate(), with the premise that in case of Ethernet, dropping a couple of packets during gadget initialization is not an issue, because it will only happen when the software is not yet ready.
But when using other functions with musb, for example, f_mass_storage or f_acm, this code path will be used not only when the software is not yet ready, but also mid-communication, and it will cause data loss. Data loss happens on gadgets that don't always have requests queued for an ep.
The timeline of a data loss:
1. A function that does not have a lot of requests pre-queued completes an OUT request. The queue is empty.
2. The next packet comes. MUSB auto-acks it, places it into the FIFO, sets RXPKTRDY, raises an interrupt.
3. musb_g_rx() is called, sees the request queue is empty, early returns.
4. The function queues the next request (f_mass_storage does it from the main fsg thread), musb_ep_restart does FLUSHFIFO, packet is lost.
5. The packet was auto-acked by MUSB, so it is not resent by the host.
Next data chunk arrives with an IRQ like so:
musb_g_rx returns, queue empty
(drivers/usb/musb/musb_gadget.c:810)
musb_g_rx from musb_interrupt
(drivers/usb/musb/musb_core.c:1786)
musb_interrupt from dsps_interrupt
(drivers/usb/musb/musb_dsps.c:389)and then it is dropped from the FIFO when usb_ep_queue() is called:
MUSB_RXCSR_FLUSHFIFO from musb_ep_restart
(drivers/usb/musb/musb_gadget.c:1174)
musb_ep_restart from musb_ep_restart_resume_work
(drivers/usb/musb/musb_gadget.c:1184)
musb_ep_restart_resume_work from musb_queue_resume_work
(drivers/usb/musb/musb_core.c:2277)
musb_queue_resume_work from musb_gadget_queue
(drivers/usb/musb/musb_gadget.c:1249)
musb_gadget_queue from usb_ep_queue
(drivers/usb/gadget/udc/core.c:306)In case of f_mass_storage, it keeps waiting for the lost data; the host then times out and resets the device. Since this is a race condition, this can be seen as data transfers taking too long, or as drives getting mounted but disappearing when data is read/written, and so on.
usb 1-8.4: reset high-speed USB device number 31 using xhci_hcd
I/O error, dev sdb, sector 240 op 0x1:(WRITE) flags 0x804800 phys_seg 30 prio class 2 Buffer I/O error on dev sdb, logical block 30, lost async page write
When testing f_acm, this is seen as byte loss on the tty. f_hid, f_printer and f_fs lose data similarly.
Testing with commit 3fc137386c46 ("usb: musb: Fix hardware lockup on first Rx endpoint request") reverted fixed these symptoms.
Move the data processing to system_bh_wq. Purpose of doing so is to honor the usb_ep_queue docstring and to run in an interrupt context, avoiding the lockup Hubert described in his commit message. Alternative way of looking at this is it "re-triggers" the interrupt, running the musb_g_rx that had to be skipped.
Such a change makes a different race condition more visible. On SMP or PREEMPT_RT, the RX interrupt can come while musb_g_giveback() is running on the same endpoint and has already dropped musb->lock; before this patch, that would be possible only when usb_ep_dequeue() runs in parallel with an interrupt, but after this patch,
it could be the workqueue item running when an interrupt comes. Handling it at that moment could lead to packets getting stuck never matched with their requests (locking up the device essentially) or requests completing but out of order.
So in musb_g_rx() and musb_gadget_queue(), also defer the RX'd data to a work item if ep->busy is set. The deferred work items are scheduled when the last musb_g_giveback() finishes and busy drops to 0.
On suspend, disable the work items; on resume, enable the work items and also schedule one work item on every enabled OUT endpoint, to be sure no work is pending from before the suspend.
Do not do any deferral of DMA completion. When musb_g_rx() is called with DMAENAB set, it means it's called from musb_dma_completion(). A DMA completion could not be happening at the same time as a workqueue non-DMA giveback (because this DMA needed to be programmed first, and programming of a DMA is only possible when all non-DMA requests ahead in the request queue already got their givebacks). And work items of DMA requests don't do givebacks. Deferring DMA completion is more work, so just don't change the behavior there.
DMA completion can still race with usb_ep_dequeue(), that's the same as without this patch. So DMA situation should not become any worse.
The issue was reproduced and the patch was tested on a BeagleBone Black (AM335x, one core, no RX DMA) with f_mass_storage, f_acm, f_hid, f_printer and f_fs. It was also verified OK before and after this patch with f_ecm and f_ncm.
This issue was also reproduced and the patch was tested on a Pine A64+ (Allwinner A64, four cores, no DMA) with both PREEMPT_DYNAMIC and PREEMPT_RT, with lockdep enabled, with f_mass_storage, f_acm, f_hid, f_printer and f_fs. PREEMPT_DYNAMIC was also tested with KASAN.
This patch was also tested with suspend to RAM on the Pine A64+, both idle and during a transfer.
--- drivers/usb/musb/musb_core.c | 3 + drivers/usb/musb/musb_gadget.c | 118 ++++++++++++++++++++++++++++++++- drivers/usb/musb/musb_gadget.h | 9 +++ 3 files changed, 127 insertions(+), 3 deletions(-)
drivers/usb/musb/musb_core.c +3 -0
@@ -2818,6 +2818,8 @@ static int musb_suspend(struct device *dev) ; musb->flush_irq_work = false; +musb_gadget_disable_rx_kicks (musb); +if (!(musb->ops->quirks &MUSB_PRESERVE_SESSION ))musb_writeb (musb->mregs,MUSB_DEVCTL ,0 );
@@ -2877,6 +2879,7 @@ static int musb_resume(struct device *dev) }spin_lock_irqsave (&musb->lock, flags); +musb_gadget_enable_rx_kicks (musb); error =musb_run_resume_work (musb);if (error) dev_err(musb->controller, "resume work failed with %i\n",
drivers/usb/musb/musb_gadget.c +115 -3
163 lines not on this page · raw message
drivers/usb/musb/musb_gadget.h +9 -0
@@ -11,6 +11,7 @@#define __MUSB_GADGET_H#include <linux/list.h> +#include <linux/workqueue.h> #if IS_ENABLED (CONFIG_USB_MUSB_GADGET ) ||IS_ENABLED (CONFIG_USB_MUSB_DUAL_ROLE )extern irqreturn_t musb_g_ep0_irq (struct musb *);
@@ -23,6 +24,8 @@ extern void musb_g_wakeup(struct musb *);extern void musb_g_disconnect (struct musb *);extern void musb_gadget_cleanup (struct musb *);extern int musb_gadget_setup (struct musb *); +void musb_gadget_disable_rx_kicks (struct musb *musb); +void musb_gadget_enable_rx_kicks (struct musb *musb);#else static inline irqreturn_t musb_g_ep0_irq (struct musb *musb)
@@ -42,6 +45,9 @@ static inline int musb_gadget_setup(struct musb *musb) {return 0 ; } + +static inline void musb_gadget_disable_rx_kicks (struct musb *musb) {} +static inline void musb_gadget_enable_rx_kicks (struct musb *musb) {}#endif enum buffer_map_state {
@@ -93,6 +99,9 @@ struct musb_ep {/* number of givebacks in progress; req_list may not be advanced */ u8 busy; +struct work_struct rx_kick; +bool rx_kick_deferred; +u8 hb_mult; };
Signature · 1 line
2.53.0
[email protected]
AI reviewRe: [PATCH v4 2/2] usb: musb: gadget: don't flush a waiting RX packet when queueing a request
Sashiko has reviewed this patch and found no issues.
Quoted 14 lines
Since commit
3fc137386c46("usb: musb: Fix hardware lockup on first Rx endpoint request"),musb_ep_restart()flushes the RX FIFO instead of callingrxstate(), with the premise that in case of Ethernet, dropping a couple of packets during gadget initialization is not an issue, because it will only happen when the software is not yet ready.But when using other functions with musb, for example,
f_mass_storageorf_acm, this code path will be used not only when the software is not yet ready, but also mid-communication, and it will cause data loss. Data loss happens on gadgets that don't always have requests queued for an ep.The timeline of a data loss:
Quoted 7 lines
1. A function that does not have a lot of requests pre-queued [ ... ]
This patch was also tested with suspend to RAM on the Pine A64+, both idle and during a transfer.Fixes 3fc137386c46 ("usb: musb: Fix hardware lockup on first Rx endpoint request")Signed-off-by Elvira Khabirova <[email protected]>
Sashiko has reviewed this patch and found no issues.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2