Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion drivers/tty/serial/qcom_geni_serial.c
Original file line number Diff line number Diff line change
Expand Up @@ -1022,8 +1022,20 @@ static void qcom_geni_serial_handle_tx_dma(struct uart_port *uport)
{
struct qcom_geni_serial_port *port = to_dev_port(uport);
struct tty_port *tport = &uport->state->port;
unsigned int fifo_len = kfifo_len(&tport->xmit_fifo);

/*
* Only advance the kfifo if it still contains the bytes that were
* transferred. uart_flush_buffer() may have run before this IRQ
* fired: it calls kfifo_reset() under the port lock, making
* fifo_len = 0 while tx_remaining remains non-zero. Calling
* uart_xmit_advance() in that case would underflow kfifo->out past
* kfifo->in, making kfifo_len() wrap to UART_XMIT_SIZE - tx_remaining
* and triggering a spurious large DMA transfer of stale data.
*/
if (fifo_len >= port->tx_remaining)
uart_xmit_advance(uport, port->tx_remaining);

uart_xmit_advance(uport, port->tx_remaining);
geni_se_tx_dma_unprep(&port->se, port->tx_dma_addr, port->tx_remaining);
port->tx_dma_addr = 0;
port->tx_remaining = 0;
Expand Down