[PATCH] serial: tegra: handle rx race

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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



[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux PPP]     [Linux FS]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Linmodem]     [Device Mapper]     [Linux Kernel for ARM]

  Powered by Linux