tegra_uart_rx_dma_complete (via DMA callback) and tegra_uart_handle_rx_dma (via uart isr) can happen concurrently. tegra_uart_rx_complete gives up the port lock temporarily to call tty_flip_buffer_push. Since tegra_uart_start_rx_dma has not been called yet in that context, tegra_uart_handle_rx_dma has the chance to operate on the same DMA cookie. This allows for the same DMA transaction to be processed twice. The solution is to postpone tty_flip_buffer_push until after the next DMA is started in both routines. That way when the lock is released in either context, the other context will operate on a new DMA transaction. Signed-off-by: Christopher Freeman <cfreeman@xxxxxxxxxx> --- drivers/tty/serial/serial-tegra.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index cf0133a..f9bd378 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -606,12 +606,6 @@ static void tegra_uart_rx_dma_complete(void *args) tegra_uart_copy_rx_to_tty(tup, port, count); tegra_uart_handle_rx_pio(tup, port); - if (tty) { - spin_unlock_irqrestore(&u->lock, flags); - tty_flip_buffer_push(port); - spin_lock_irqsave(&u->lock, flags); - tty_kref_put(tty); - } tegra_uart_start_rx_dma(tup); /* Activate flow control to start transfer */ @@ -620,6 +614,10 @@ static void tegra_uart_rx_dma_complete(void *args) done: spin_unlock_irqrestore(&u->lock, flags); + if (tty) { + tty_flip_buffer_push(port); + tty_kref_put(tty); + } } static void tegra_uart_handle_rx_dma(struct tegra_uart_port *tup, @@ -644,16 +642,17 @@ static void tegra_uart_handle_rx_dma(struct tegra_uart_port *tup, tegra_uart_copy_rx_to_tty(tup, port, count); tegra_uart_handle_rx_pio(tup, port); + tegra_uart_start_rx_dma(tup); + + if (tup->rts_active) + set_rts(tup, true); + if (tty) { spin_unlock_irqrestore(&u->lock, *flags); tty_flip_buffer_push(port); spin_lock_irqsave(&u->lock, *flags); tty_kref_put(tty); } - tegra_uart_start_rx_dma(tup); - - if (tup->rts_active) - set_rts(tup, true); } static int tegra_uart_start_rx_dma(struct tegra_uart_port *tup) -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-serial" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html