serial: 8250: fix XOFF/XON sending when DMA is used
authorIlpo Järvinen <ilpo.jarvinen@linux.intel.com>
Mon, 14 Mar 2022 09:14:32 +0000 (11:14 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 18 Mar 2022 12:30:54 +0000 (13:30 +0100)
When 8250 UART is using DMA, x_char (XON/XOFF) is never sent
to the wire. After this change, x_char is injected correctly.

Create uart_xchar_out() helper for sending the x_char out and
accounting related to it. It seems that almost every driver
does these same steps with x_char. Except for 8250, however,
almost all currently lack .serial_out so they cannot immediately
take advantage of this new helper.

The downside of this patch is that it might reintroduce
the problems some devices faced with mixed DMA/non-DMA transfer
which caused revert f967fc8f165f (Revert "serial: 8250_dma:
don't bother DMA with small transfers"). However, the impact
should be limited to cases with XON/XOFF (that didn't work
with DMA capable devices to begin with so this problem is not
very likely to cause a major issue, if any at all).

Fixes: 9ee4b83e51f74 ("serial: 8250: Add support for dmaengine")
Reported-by: Gilles Buloz <gilles.buloz@kontron.com>
Tested-by: Gilles Buloz <gilles.buloz@kontron.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://lore.kernel.org/r/20220314091432.4288-2-ilpo.jarvinen@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/8250/8250_dma.c
drivers/tty/serial/8250/8250_port.c
drivers/tty/serial/serial_core.c
include/linux/serial_core.h

index 890fa7d..b3c3f7e 100644 (file)
@@ -64,10 +64,19 @@ int serial8250_tx_dma(struct uart_8250_port *p)
        struct uart_8250_dma            *dma = p->dma;
        struct circ_buf                 *xmit = &p->port.state->xmit;
        struct dma_async_tx_descriptor  *desc;
+       struct uart_port                *up = &p->port;
        int ret;
 
-       if (dma->tx_running)
+       if (dma->tx_running) {
+               if (up->x_char) {
+                       dmaengine_pause(dma->txchan);
+                       uart_xchar_out(up, UART_TX);
+                       dmaengine_resume(dma->txchan);
+               }
                return 0;
+       } else if (up->x_char) {
+               uart_xchar_out(up, UART_TX);
+       }
 
        if (uart_tx_stopped(&p->port) || uart_circ_empty(xmit)) {
                /* We have been called from __dma_tx_complete() */
index 2670268..318af6f 100644 (file)
@@ -1822,9 +1822,7 @@ void serial8250_tx_chars(struct uart_8250_port *up)
        int count;
 
        if (port->x_char) {
-               serial_out(up, UART_TX, port->x_char);
-               port->icount.tx++;
-               port->x_char = 0;
+               uart_xchar_out(port, UART_TX);
                return;
        }
        if (uart_tx_stopped(port)) {
index a1688a3..6a8963c 100644 (file)
@@ -642,6 +642,20 @@ static void uart_flush_buffer(struct tty_struct *tty)
 }
 
 /*
+ * This function performs low-level write of high-priority XON/XOFF
+ * character and accounting for it.
+ *
+ * Requires uart_port to implement .serial_out().
+ */
+void uart_xchar_out(struct uart_port *uport, int offset)
+{
+       serial_port_out(uport, offset, uport->x_char);
+       uport->icount.tx++;
+       uport->x_char = 0;
+}
+EXPORT_SYMBOL_GPL(uart_xchar_out);
+
+/*
  * This function is used to send a high-priority XON/XOFF character to
  * the device
  */
index 14ae35f..d4828e6 100644 (file)
@@ -458,6 +458,8 @@ extern void uart_handle_cts_change(struct uart_port *uport,
 extern void uart_insert_char(struct uart_port *port, unsigned int status,
                 unsigned int overrun, unsigned int ch, unsigned int flag);
 
+void uart_xchar_out(struct uart_port *uport, int offset);
+
 #ifdef CONFIG_MAGIC_SYSRQ_SERIAL
 #define SYSRQ_TIMEOUT  (HZ * 5)