Skip to content

Commit 57e9af7

Browse files
ij-intelgregkh
authored andcommitted
serial: 8250_dma: Fix DMA Rx rearm race
As DMA Rx can be completed from two places, it is possible that DMA Rx completes before DMA completion callback had a chance to complete it. Once the previous DMA Rx has been completed, a new one can be started on the next UART interrupt. The following race is possible (uart_unlock_and_check_sysrq_irqrestore() replaced with spin_unlock_irqrestore() for simplicity/clarity): CPU0 CPU1 dma_rx_complete() serial8250_handle_irq() spin_lock_irqsave(&port->lock) handle_rx_dma() serial8250_rx_dma_flush() __dma_rx_complete() dma->rx_running = 0 // Complete DMA Rx spin_unlock_irqrestore(&port->lock) serial8250_handle_irq() spin_lock_irqsave(&port->lock) handle_rx_dma() serial8250_rx_dma() dma->rx_running = 1 // Setup a new DMA Rx spin_unlock_irqrestore(&port->lock) spin_lock_irqsave(&port->lock) // sees dma->rx_running = 1 __dma_rx_complete() dma->rx_running = 0 // Incorrectly complete // running DMA Rx This race seems somewhat theoretical to occur for real but handle it correctly regardless. Check what is the DMA status before complething anything in __dma_rx_complete(). Reported-by: Gilles BULOZ <[email protected]> Tested-by: Gilles BULOZ <[email protected]> Fixes: 9ee4b83 ("serial: 8250: Add support for dmaengine") Cc: [email protected] Signed-off-by: Ilpo Järvinen <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 3135281 commit 57e9af7

File tree

1 file changed

+10
-2
lines changed

1 file changed

+10
-2
lines changed

drivers/tty/serial/8250/8250_dma.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,23 @@ static void __dma_rx_complete(struct uart_8250_port *p)
4343
struct uart_8250_dma *dma = p->dma;
4444
struct tty_port *tty_port = &p->port.state->port;
4545
struct dma_tx_state state;
46+
enum dma_status dma_status;
4647
int count;
4748

48-
dma->rx_running = 0;
49-
dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
49+
/*
50+
* New DMA Rx can be started during the completion handler before it
51+
* could acquire port's lock and it might still be ongoing. Don't to
52+
* anything in such case.
53+
*/
54+
dma_status = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
55+
if (dma_status == DMA_IN_PROGRESS)
56+
return;
5057

5158
count = dma->rx_size - state.residue;
5259

5360
tty_insert_flip_string(tty_port, dma->rx_buf, count);
5461
p->port.icount.rx += count;
62+
dma->rx_running = 0;
5563

5664
tty_flip_buffer_push(tty_port);
5765
}

0 commit comments

Comments
 (0)