Patch is tested and based on 3.19-rc2. On Wed, Jan 14, 2015 at 11:47 AM, <prasad.koya@xxxxxxxxx> wrote: > From 859779819fe0490daaddeff5cb3cdda7a4372584 Mon Sep 17 00:00:00 2001 > From: Prasad Koya <prasad@xxxxxxxxxx> > Date: Wed, 14 Jan 2015 11:07:41 -0800 > Subject: [PATCH 1/1] tty, serial, 8250: to avoid recv fifo overrun, read rx fifo during xmit > > At slow baud rates, say 9600, it is easy to run into tty buffer overrun > as interrupts could get disabled for a long time. To avoid that, if > UART_LSR_DR is set, pick up bytes from rx fifo while waiting for tx > buffer to be empty. Stop buffering if port->sysrq is true or > oops_in_progress is set. > > George Spelvin reworked original patch quite a bit. > > Tested on 16550 UART with 16byte FIFO > > Signed-off-by: Prasad Koya <prasad@xxxxxxxxxx> > --- > drivers/tty/serial/8250/8250_core.c | 65 +++++++++++++++++++++++++++++++++---- > drivers/tty/serial/8250/8250_fsl.c | 2 +- > include/linux/serial_8250.h | 3 +- > include/linux/serial_core.h | 2 +- > 4 files changed, 63 insertions(+), 9 deletions(-) > > diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c > index 11c6685..a9adf9d 100644 > --- a/drivers/tty/serial/8250/8250_core.c > +++ b/drivers/tty/serial/8250/8250_core.c > @@ -1434,9 +1434,12 @@ static void serial8250_enable_ms(struct uart_port *port) > * serial8250_rx_chars: processes according to the passed in LSR > * value, and returns the remaining LSR bits not handled > * by this Rx routine. > + * no_sysrq is normally false, but when set it indicates we should stop > + * processing input after receiving a break, rather than passing the > + * next character to uart_handle_sysrq_char. > */ > unsigned char > -serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr) > +serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr, bool no_sysrq) > { > struct uart_port *port = &up->port; > unsigned char ch; > @@ -1472,8 +1475,11 @@ serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr) > * may get masked by ignore_status_mask > * or read_status_mask. > */ > - if (uart_handle_break(port)) > + if (uart_handle_break(port)) { > + if (no_sysrq) > + max_count = 0; > goto ignore_char; > + } > } else if (lsr & UART_LSR_PE) > port->icount.parity++; > else if (lsr & UART_LSR_FE) > @@ -1609,7 +1615,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) > dma_err = up->dma->rx_dma(up, iir); > > if (!up->dma || dma_err) > - status = serial8250_rx_chars(up, status); > + status = serial8250_rx_chars(up, status, false); > } > serial8250_modem_status(up); > if ((!up->dma || (up->dma && up->dma->tx_err)) && > @@ -1962,9 +1968,30 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state) > } > > /* > + * In some special situations, we allow console output without necessarily > + * locking the port. The same situations also suppress processing input > + * during polling output, because we aren't ready to deal with it. > + * So just hope the hardware FIFO can hold until interrupts are > + * re-enabled. > + */ > +static bool emergency_situation(const struct uart_port *port) > +{ > +#ifdef SUPPORT_SYSRQ > + return port->sysrq != 0 || oops_in_progress; > +#else > + (void)port; > + return oops_in_progress; > +#endif > +} > + > +/* > * Wait for transmitter & holding register to empty > + * If rx is true, then also poll for received characters if it's > + * safe to process them. This is only used in the SUPPORT_SYSRQ > + * case, and hopefully the compiler can figure out it's dead code > + * if that's not the case. > */ > -static void wait_for_xmitr(struct uart_8250_port *up, int bits) > +static void __wait_for_xmitr(struct uart_8250_port *up, int bits, bool rx) > { > unsigned int status, tmout = 10000; > > @@ -1978,6 +2005,11 @@ static void wait_for_xmitr(struct uart_8250_port *up, int bits) > break; > if (--tmout == 0) > break; > + > + if (rx && status & (UART_LSR_DR | UART_LSR_BI) && > + !emergency_situation(&up->port)) > + serial8250_rx_chars(up, status, true); > + > udelay(1); > } > > @@ -1989,12 +2021,29 @@ static void wait_for_xmitr(struct uart_8250_port *up, int bits) > up->msr_saved_flags |= msr & MSR_SAVE_FLAGS; > if (msr & UART_MSR_CTS) > break; > + > + if (rx && !emergency_situation(&up->port)) { > + status = serial_in(up, UART_LSR); > + > + up->lsr_saved_flags |= status & LSR_SAVE_FLAGS; > + if (status & (UART_LSR_DR | UART_LSR_BI)) > + serial8250_rx_chars(up, status, true); > + } > + > udelay(1); > touch_nmi_watchdog(); > } > } > } > > +/* > + * Wait for transmitter & holding register to empty > + */ > +static void wait_for_xmitr(struct uart_8250_port *up, int bits) > +{ > + __wait_for_xmitr(up, bits, false); > +} > + > #ifdef CONFIG_CONSOLE_POLL > /* > * Console polling routines for writing and reading from the uart while > @@ -3168,7 +3217,7 @@ static void serial8250_console_putchar(struct uart_port *port, int ch) > { > struct uart_8250_port *up = up_to_u8250p(port); > > - wait_for_xmitr(up, UART_LSR_THRE); > + __wait_for_xmitr(up, UART_LSR_THRE, true); > serial_port_out(port, UART_TX, ch); > } > > @@ -3176,6 +3225,10 @@ static void serial8250_console_putchar(struct uart_port *port, int ch) > * Print a string to the serial port trying not to disturb > * any possible real use of the port... > * > + * In order to avoid dropped input if the output is longer than > + * the available hardware FIFO space, we try to handle incoming > + * characters during this polling loop. > + * > * The console_lock must be held when we get here. > */ > static void > @@ -3214,7 +3267,7 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count) > * Finally, wait for transmitter to become empty > * and restore the IER > */ > - wait_for_xmitr(up, BOTH_EMPTY); > + __wait_for_xmitr(up, BOTH_EMPTY, true); > serial_port_out(port, UART_IER, ier); > > /* > diff --git a/drivers/tty/serial/8250/8250_fsl.c b/drivers/tty/serial/8250/8250_fsl.c > index c0533a5..25f0aa3 100644 > --- a/drivers/tty/serial/8250/8250_fsl.c > +++ b/drivers/tty/serial/8250/8250_fsl.c > @@ -49,7 +49,7 @@ int fsl8250_handle_irq(struct uart_port *port) > lsr = orig_lsr = up->port.serial_in(&up->port, UART_LSR); > > if (lsr & (UART_LSR_DR | UART_LSR_BI)) > - lsr = serial8250_rx_chars(up, lsr); > + lsr = serial8250_rx_chars(up, lsr, false); > > serial8250_modem_status(up); > > diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h > index e02acf0..8544bc5 100644 > --- a/include/linux/serial_8250.h > +++ b/include/linux/serial_8250.h > @@ -128,7 +128,8 @@ extern void serial8250_do_pm(struct uart_port *port, unsigned int state, > unsigned int oldstate); > extern int fsl8250_handle_irq(struct uart_port *port); > int serial8250_handle_irq(struct uart_port *port, unsigned int iir); > -unsigned char serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr); > +unsigned char serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr, > + bool no_sysrq); > void serial8250_tx_chars(struct uart_8250_port *up); > unsigned int serial8250_modem_status(struct uart_8250_port *up); > > diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h > index 057038c..c93c5aa 100644 > --- a/include/linux/serial_core.h > +++ b/include/linux/serial_core.h > @@ -410,7 +410,7 @@ uart_handle_sysrq_char(struct uart_port *port, unsigned int ch) > if (port->sysrq) { > if (ch && time_before(jiffies, port->sysrq)) { > handle_sysrq(ch); > - port->sysrq = 0; > + port->sysrq = 0; /* Must be AFTER handle_sysrq */ > return 1; > } > port->sysrq = 0; > -- > 1.8.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 -- 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