Skip to content

Commit 9e512ea

Browse files
johnkeepinggregkh
authored andcommitted
serial: 8250: Fix fifo underflow on flush
When flushing the serial port's buffer, uart_flush_buffer() calls kfifo_reset() but if there is an outstanding DMA transfer then the completion function will consume data from the kfifo via uart_xmit_advance(), underflowing and leading to ongoing DMA as the driver tries to transmit another 2^32 bytes. This is readily reproduced with serial-generic and amidi sending even short messages as closing the device on exit will wait for the fifo to drain and in the underflow case amidi hangs for 30 seconds on exit in tty_wait_until_sent(). A trace of that gives: kworker/1:1-84 [001] 51.769423: bprint: serial8250_tx_dma: tx_size=3 fifo_len=3 amidi-763 [001] 51.769460: bprint: uart_flush_buffer: resetting fifo irq/21-fe530000-76 [000] 51.769474: bprint: __dma_tx_complete: tx_size=3 irq/21-fe530000-76 [000] 51.769479: bprint: serial8250_tx_dma: tx_size=4096 fifo_len=4294967293 irq/21-fe530000-76 [000] 51.781295: bprint: __dma_tx_complete: tx_size=4096 irq/21-fe530000-76 [000] 51.781301: bprint: serial8250_tx_dma: tx_size=4096 fifo_len=4294963197 irq/21-fe530000-76 [000] 51.793131: bprint: __dma_tx_complete: tx_size=4096 irq/21-fe530000-76 [000] 51.793135: bprint: serial8250_tx_dma: tx_size=4096 fifo_len=4294959101 irq/21-fe530000-76 [000] 51.804949: bprint: __dma_tx_complete: tx_size=4096 Since the port lock is held in when the kfifo is reset in uart_flush_buffer() and in __dma_tx_complete(), adding a flush_buffer hook to adjust the outstanding DMA byte count is sufficient to avoid the kfifo underflow. Fixes: 9ee4b83 ("serial: 8250: Add support for dmaengine") Cc: stable <[email protected]> Signed-off-by: John Keeping <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 0f3fd9c commit 9e512ea

File tree

3 files changed

+27
-0
lines changed

3 files changed

+27
-0
lines changed

drivers/tty/serial/8250/8250.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ static inline int is_omap1510_8250(struct uart_8250_port *pt)
374374

375375
#ifdef CONFIG_SERIAL_8250_DMA
376376
extern int serial8250_tx_dma(struct uart_8250_port *);
377+
extern void serial8250_tx_dma_flush(struct uart_8250_port *);
377378
extern int serial8250_rx_dma(struct uart_8250_port *);
378379
extern void serial8250_rx_dma_flush(struct uart_8250_port *);
379380
extern int serial8250_request_dma(struct uart_8250_port *);
@@ -406,6 +407,7 @@ static inline int serial8250_tx_dma(struct uart_8250_port *p)
406407
{
407408
return -1;
408409
}
410+
static inline void serial8250_tx_dma_flush(struct uart_8250_port *p) { }
409411
static inline int serial8250_rx_dma(struct uart_8250_port *p)
410412
{
411413
return -1;

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,22 @@ int serial8250_tx_dma(struct uart_8250_port *p)
149149
return ret;
150150
}
151151

152+
void serial8250_tx_dma_flush(struct uart_8250_port *p)
153+
{
154+
struct uart_8250_dma *dma = p->dma;
155+
156+
if (!dma->tx_running)
157+
return;
158+
159+
/*
160+
* kfifo_reset() has been called by the serial core, avoid
161+
* advancing and underflowing in __dma_tx_complete().
162+
*/
163+
dma->tx_size = 0;
164+
165+
dmaengine_terminate_async(dma->rxchan);
166+
}
167+
152168
int serial8250_rx_dma(struct uart_8250_port *p)
153169
{
154170
struct uart_8250_dma *dma = p->dma;

drivers/tty/serial/8250/8250_port.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2555,6 +2555,14 @@ static void serial8250_shutdown(struct uart_port *port)
25552555
serial8250_do_shutdown(port);
25562556
}
25572557

2558+
static void serial8250_flush_buffer(struct uart_port *port)
2559+
{
2560+
struct uart_8250_port *up = up_to_u8250p(port);
2561+
2562+
if (up->dma)
2563+
serial8250_tx_dma_flush(up);
2564+
}
2565+
25582566
static unsigned int serial8250_do_get_divisor(struct uart_port *port,
25592567
unsigned int baud,
25602568
unsigned int *frac)
@@ -3244,6 +3252,7 @@ static const struct uart_ops serial8250_pops = {
32443252
.break_ctl = serial8250_break_ctl,
32453253
.startup = serial8250_startup,
32463254
.shutdown = serial8250_shutdown,
3255+
.flush_buffer = serial8250_flush_buffer,
32473256
.set_termios = serial8250_set_termios,
32483257
.set_ldisc = serial8250_set_ldisc,
32493258
.pm = serial8250_pm,

0 commit comments

Comments
 (0)