Disclaimer: This report was prepared with AI assistance (Claude Code). I reviewed it, verified the code citations against master myself, and validated the behaviour on real hardware where noted before filing.
Vs arch/arm/src/rp23xx/rp23xx_spi.c at master 50f91ef502.
Description
The SPI DMA exchange path waits for completion with two back-to-back
untimed, uninterruptible semaphore waits (l. 1196 and l. 1201):
if (nxsem_wait_uninterruptible(&priv->dmasem) != OK)
{
spierr("dma error\n");
}
if (nxsem_wait_uninterruptible(&priv->dmasem) != OK)
{
spierr("dma error\n");
}
If a DMA transfer never completes, the calling thread sleeps forever with
no escape. DMA is on by default (RP23XX_DMAC default y,
RP23XX_SPI_DMA default y, threshold 4), so effectively all bulk SPI
transfers take this path.
Impact observed on real hardware
With a W5500 Ethernet controller on SPI0 (W5500-EVB-Pico2), network
drivers run their transfers from the LP work queue while holding the
network lock. When the wait stalled, the LP worker slept forever holding
net_lock: ping stopped, every shell command touching the network
(ifconfig, ...) blocked immediately after echo, and even the driver's
own TX-timeout recovery could not run (it is queued to the same blocked
LP worker). The system was otherwise alive (non-network shell paths
responsive).
A/B on the same board and workload: with CONFIG_RP23XX_SPI_DMA=n the
identical external event left the system fully healthy. (The same
untimed-wait pattern appears worth checking in rp2040_spi.c, from which
this port derives.)
Suggested fix
Use a bounded wait (e.g. nxsem_tickwait_uninterruptible() with a
transfer-size-derived timeout); on timeout, stop the DMA channels, log,
and fall back to polled PIO or return an error — degrade instead of
deadlocking.
Disclosure: found during an AI-assisted (Claude Code) audit; the A/B
observation is from my own hardware testing.
Vs
arch/arm/src/rp23xx/rp23xx_spi.cat master50f91ef502.Description
The SPI DMA exchange path waits for completion with two back-to-back
untimed, uninterruptible semaphore waits (l. 1196 and l. 1201):
If a DMA transfer never completes, the calling thread sleeps forever with
no escape. DMA is on by default (
RP23XX_DMACdefault y,RP23XX_SPI_DMAdefault y, threshold 4), so effectively all bulk SPItransfers take this path.
Impact observed on real hardware
With a W5500 Ethernet controller on SPI0 (W5500-EVB-Pico2), network
drivers run their transfers from the LP work queue while holding the
network lock. When the wait stalled, the LP worker slept forever holding
net_lock: ping stopped, every shell command touching the network(
ifconfig, ...) blocked immediately after echo, and even the driver'sown TX-timeout recovery could not run (it is queued to the same blocked
LP worker). The system was otherwise alive (non-network shell paths
responsive).
A/B on the same board and workload: with
CONFIG_RP23XX_SPI_DMA=ntheidentical external event left the system fully healthy. (The same
untimed-wait pattern appears worth checking in
rp2040_spi.c, from whichthis port derives.)
Suggested fix
Use a bounded wait (e.g.
nxsem_tickwait_uninterruptible()with atransfer-size-derived timeout); on timeout, stop the DMA channels, log,
and fall back to polled PIO or return an error — degrade instead of
deadlocking.
Disclosure: found during an AI-assisted (Claude Code) audit; the A/B
observation is from my own hardware testing.