ricardgb opened a new issue, #19472:
URL: https://github.com/apache/nuttx/issues/19472
## Description
On RP2350 (Cortex-M33), the USB device driver
(`arch/arm/src/rp23xx/rp23xx_usbdev.c`)
re-arms an endpoint by writing the buffer-control **AVAILABLE** bit
**without a memory
barrier ordering it after the preceding `BUFF_STATUS` clear**. These are two
writes to two
different peripheral regions:
- the `BUFF_STATUS` clear (done in `rp23xx_usbintr_buffstat` when handling a
completion)
targets the **USB controller register block at `0x50110000`**
(`BUFF_STATUS` @ `+0x58`);
- setting **AVAILABLE** in the endpoint buffer-control word targets **USB
DPSRAM at
`0x50100000`**.
On the Cortex-M33 bus fabric, writes to different Device regions may be
observed out of
order unless separated by a `DMB`. In a **non-SMP** build (`CONFIG_SMP`
unset)
`spin_lock_irqsave` compiles to a barrier-free `up_irq_save`, so nothing
orders these two
stores.
### The race (lost IN completion)
When re-arming the next IN buffer directly from a completion handler under
back-to-back TX:
1. AVAILABLE (DPSRAM) is observed by the controller **before** the
`BUFF_STATUS` clear
(register block) lands.
2. The controller transmits the next IN packet and **latches its completion
by setting the
`BUFF_STATUS` bit**.
3. The late-arriving clear then **wipes that just-set bit**.
The completion edge is lost: the IN endpoint never raises another buffer
interrupt, the
class `wrcomplete` callback never runs, and (for CDC-NCM) `wrreq_idle` is
never reposted, so
transmit **wedges permanently**. The controller-register note in the header
even warns:
*"clearing the buffer status bit may instantly re-set it on the next clock
cycle."*
## Steps to reproduce
- RP2350 (Raspberry Pi Pico 2 W), CDC-NCM composite acting as a USB-NIC.
- Enable `CONFIG_NET_TCP_WRITE_BUFFERS=y` so TCP transmit is bursty (a
single `txavail`
poll drains many segments back-to-back), keeping the IN pipe continuously
full.
- Drive sustained/concurrent HTTP downloads.
**Actual:** transmit wedges intermittently (worse with denser TX). The core
is idle
(`up_idle`), **no fault**, `wrreq_idle` never reposted. Over SWD in the
wedged state:
`BUFF_STATUS = 0`, USB `INTS = 0`, the bulk-IN buffer-control word shows the
buffer
**prepared but AVAILABLE clear** (e.g. `0x0000_2024` = PID + length, no
AVAILABLE), i.e. a
lost completion edge — hardware quiescent while the driver waits forever.
**Expected:** sustained TX never wedges.
## Why RP2040 is unaffected
The identical logic runs on RP2040 (Cortex-M0+), whose far stricter/simpler
store ordering
does not exhibit this reorder — so this is RP2350/Cortex-M33-specific.
## Proposed fix
Insert a `UP_DMB()` (and `#include <arch/barriers.h>`) immediately before
setting the
AVAILABLE bit on the re-arm path, so every preceding controller-register
write (the
`BUFF_STATUS` clear) is ordered ahead of the DPSRAM AVAILABLE store.
This was root-caused and fixed on an RP2350 (Pico 2 W) CDC-NCM USB-NIC: with
the barrier,
a soak of 144 dense/concurrent HTTP downloads at ~486 KB/s ran with zero
wedges (previously
transmit hung within a handful of requests). A patch is available; I can
open a PR against
whichever revision of the RP2350 IN-endpoint re-arm path is preferred (the
exact call site
depends on recent driver refactors — refs the USB endpoint work in
#19378/#19450/#19451 and
the CDC-NCM TX fixes in #19469/#19470).
## Environment
- Chip: RP2350 (Cortex-M33), non-SMP.
- Board: Raspberry Pi Pico 2 W.
- NuttX: current `master`.
---
*Disclosure: this root-cause analysis was performed with the assistance of
an AI agent
(Anthropic's Claude, via Claude Code), operated and directed by the
submitter, and validated
on-hardware before filing.*
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]