Re: [PATCH v7 5/5] serial: 8250: retire canceled RX DMA before reusing the receive buffer
flat view
From: Raul E Rangel <hidden>
Date: 2026-10-28 20:48:55
Also in:
imx, linux-arm-kernel, linux-serial, lkml
Hi James, In serial8250_rx_dma_flush():
quoted hunk ↗ jump to hunk
+ ret = dmaengine_pause(dma->rxchan); + status = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); + /* A completed transfer may reject pause before its callback gets the lock. */ + if (ret && status != DMA_COMPLETE) + goto recover;
Checking `status != DMA_COMPLETE` assumes that whenever the hardware DMA channel has finished transferring the buffer (causing dmaengine_pause() to fail because the channel is no longer active), dmaengine_tx_status() will already return DMA_COMPLETE. However, dma_cookie_status() only returns DMA_COMPLETE after the DMA driver's interrupt handler has called dma_cookie_complete() / vchan_cookie_complete(). Because serial8250_rx_dma_flush() runs under uart_port_lock_irqsave() in the UART ISR, if the RX DMA transfer finishes in hardware just before dmaengine_pause() is called (or while local IRQs are masked on the same CPU before the DMAC hardirq is serviced): - On dw-axi-dmac (drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c, used with 8250_dw), hardware clears CH_EN in DMAC_CHEN when the transfer completes, so dma_chan_pause() times out polling DWAXIDMAC_IRQ_SUSPENDED and returns -EAGAIN, while dma_chan_tx_status() still returns DMA_IN_PROGRESS because dw_axi_dma_interrupt() has not called vchan_cookie_complete() yet (and dma_chan_tx_status() also reports state.residue = length for non-cyclic transfers until dw_axi_dma_interrupt() runs). - Similarly, tegra_adma_pause() (drivers/dma/tegra210-adma.c:696) returns -EBUSY and omap_dma_pause() (drivers/dma/ti/omap-dma.c:513) returns -EINVAL if the hardware channel cleared CCR_ENABLE before the DMA ISR ran, while dma_cookie_status() still returns DMA_IN_PROGRESS. In that window, `if (ret && status != DMA_COMPLETE)` jumps to `recover:`, dropping up to 4 KB of received data and permanently disabling RX DMA (dma->rx_err = true) on a normal transfer completion. Also, on a related shutdown note in drivers/tty/serial/8250/8250_omap.c: omap_8250_shutdown() (line 775) still calls omap_8250_rx_dma_flush(up) before taking port->lock to set up->dma->shutting_down = true and clear UART_IER = 0 (lines 783-786). Because __dma_rx_do_complete() clears UART_OMAP_IER2_RHR_IT_DIS and dma->rx_running = 0, an RX interrupt or callback in that window can see shutting_down == false and restart RX DMA right before serial8250_release_dma(up). We also published the TLA+ model and TLC counterexample trace (Serial8250RxDmaV7_HwDoneBeforeIrq.cfg) here: https://github.com/ismell/linux-8250-dma-tla Thanks, Raul