[PATCH v9 4/8] serial: 8250_omap: quiesce DMA IRQ handling before shutdown
James Hilliard
james.hilliard1 at gmail.com
Mon Nov 2 15:14:31 PST 2026
disable_irq_nosync() masks the UART interrupt without waiting for a
running handler. That handler can have observed up->dma and continue
using it while shutdown releases the DMA channels and clears the pointer.
Synchronizing the IRQ later in serial core does not protect those accesses.
Publish the DMA shutdown gate under the port lock before the final RX
flush and interrupt masking. Have DMA IRQ handling check the same gate
under the lock, so a queued interrupt cannot restart receive processing
after the final flush.
Drain the UART IRQ outside the port lock before releasing DMA state.
The lock excludes competing RX processing during the flush, while
disable_irq() also waits for a handler that observed up->dma before
acquiring the lock.
Fixes: fef4f600319e ("serial: 8250: omap: Fix life cycle issues for interrupt handlers")
Signed-off-by: James Hilliard <james.hilliard1 at gmail.com>
---
drivers/tty/serial/8250/8250_omap.c | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index 322403cec2de..fc1a83d6fa7c 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -771,10 +771,6 @@ static void omap_8250_shutdown(struct uart_port *port)
guard(serial8250_rpm)(up);
flush_work(&priv->qos_work);
- if (up->dma) {
- guard(uart_port_lock_irq)(port);
- omap_8250_rx_dma_flush(up);
- }
serial_out(up, UART_OMAP_WER, 0);
if (priv->habit & UART_HAS_EFR2)
@@ -782,13 +778,16 @@ static void omap_8250_shutdown(struct uart_port *port)
/* Synchronize UART_IER access against the console. */
scoped_guard(uart_port_lock_irq, port) {
- if (up->dma)
+ if (up->dma) {
up->dma->shutting_down = true;
+ omap_8250_rx_dma_flush(up);
+ }
up->ier = 0;
serial_out(up, UART_IER, 0);
}
- disable_irq_nosync(port->irq);
+ /* An in-flight handler may still be using up->dma. */
+ disable_irq(port->irq);
serial8250_release_dma(up);
up->dma = NULL;
@@ -1330,6 +1329,11 @@ static int omap_8250_dma_handle_irq(struct uart_port *port)
uart_port_lock(port);
+ if (up->dma->shutting_down) {
+ uart_port_unlock(port);
+ return IRQ_HANDLED;
+ }
+
status = serial_port_in(port, UART_LSR);
if ((iir & 0x3f) != UART_IIR_THRI) {
--
2.53.0
More information about the linux-arm-kernel
mailing list