[PATCH 1/1] tty, serial, 8250: to avoid recv fifo overrun, read rx fifo during xmit

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

 



Hi

Below version is tested with 3.18 kernel on 16550 UART.

- Added a new variable 'writing_to_con' to 'struct uart_8250_port'.
This would be set to 1 inside serial8250_console_write() only if
port_sysrq is 0 and oops_in_progress is 0 and "receive interrupts" are
enabled.

- uart_console_write() is called to send out whole printk message as
earlier. wait_for_xmitr() invokes serial8250_rx_chars() only if
writing_to_con is set and if UART_LSR_DR is set. so instead of waiting
with usleep(1) it now buffers chars from receive queue.

- if serial_8250_chars() is called with writing_to_con=1,
serial8250_rx_chars() would read one char from UART rx buffer.

- as long as serial8250_rx_chars() does not see brk i.e., sysrq is not
set, it could buffer. if we split this functionality, driver would have
to buffer each char and flags associated with it. with this approach,
serial8250_rx_chars() will buffer "regular" chars but anything after
sysrq is left to be processed after serial8250_console_write()
returns.

Thanks.


>From 46d5da6069296132d3e9f505715ab7f30701b2a5 Mon Sep 17 00:00:00 2001
From: Prasad Koya <prasad@xxxxxxxxxx>
Date: Tue, 9 Dec 2014 22:53:31 -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, buffer char from rx fifo. Stop buffering if
port->sysrq is true. wait_for_xmitr() could pick up a byte from FIFO
(if UART_LSR_DR is set) rather than calling udelay(1).

Signed-off-by: Prasad Koya <prasad@xxxxxxxxxx>
---
 drivers/tty/serial/8250/8250_core.c | 26 ++++++++++++++++++++++++--
 include/linux/serial_8250.h         |  2 ++
 2 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_core.c
b/drivers/tty/serial/8250/8250_core.c
index ca5cfdc..b3d3eda 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -1422,7 +1422,7 @@ serial8250_rx_chars(struct uart_8250_port *up,
unsigned char lsr)
 {
        struct uart_port *port = &up->port;
        unsigned char ch;
-       int max_count = 256;
+       int max_count = up->writing_to_con ? 1 : 256;
        char flag;

        do {
@@ -1482,6 +1482,12 @@ serial8250_rx_chars(struct uart_8250_port *up,
unsigned char lsr)
                uart_insert_char(port, lsr, UART_LSR_OE, ch, flag);

 ignore_char:
+               /*
+                * We see BRK while buffering rx bytes. Stop buffering and
+                * let SysRq or SAK handling be done after console_write
+                */
+               if (port->sysrq && up->writing_to_con)
+                       break;
                lsr = serial_in(up, UART_LSR);
        } while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0));
        spin_unlock(&port->lock);
@@ -1959,7 +1965,15 @@ static void wait_for_xmitr(struct
uart_8250_port *up, int bits)
                        break;
                if (--tmout == 0)
                        break;
-               udelay(1);
+               /*
+                * We are writing to console with interrupts disabled.
+                * read recv FIFO to avoid buffer overrun
+                */
+               if (up->writing_to_con && (status & UART_LSR_DR))
+                       serial8250_rx_chars(up, status);
+               else
+                       udelay(1);
+
        }

        /* Wait up to 1s for flow control if necessary */
@@ -3102,6 +3116,7 @@ static void __init serial8250_isa_init_ports(void)
                init_timer(&up->timer);
                up->timer.function = serial8250_timeout;
                up->cur_iotype = 0xFF;
+               up->writing_to_con = 0;

                /*
                 * ALPHA_KLUDGE_MCR needs to be killed.
@@ -3213,8 +3228,15 @@ serial8250_console_write(struct console *co,
const char *s, unsigned int count)
        else
                serial_port_out(port, UART_IER, 0);

+       /* Is UART configured to send interrupts for received data */
+       if (((ier & UART_IER_RDI) == UART_IER_RDI) && !oops_in_progress &&
+                       !port->sysrq)
+               up->writing_to_con = 1;
+
        uart_console_write(port, s, count, serial8250_console_putchar);

+       if (up->writing_to_con)
+               up->writing_to_con = 0;
        /*
         *      Finally, wait for transmitter to become empty
         *      and restore the IER
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index 3df10d5..1ad79e2 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -96,6 +96,8 @@ struct uart_8250_port {
 #define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA
        unsigned char           msr_saved_flags;

+       /* Write to console on this port now */
+       unsigned char           writing_to_con;
        struct uart_8250_dma    *dma;
        struct serial_rs485     rs485;

On Fri, Oct 3, 2014 at 5:57 AM, Peter Hurley <peter@xxxxxxxxxxxxxxxxxx> wrote:
> Hi Prasad,
>
> On 09/30/2014 01:30 PM, Prasad Koya wrote:
>> Hi Ted
>>
>> At 9600 baud, we are seeing buffer overruns on our 16550A UARTs. If a
>> command, say 40 chars long, is sent to console and if printk to
>> console happens at same time, we running into receive buffer overrun.
>> Thats because, at 9600 baud, it takes about a millisecond to xmit 1
>> char and with receive FIFO only 16 chars deep, it is very easy to
>> cause receive buffer overrun.
>>
>> To get around that, I am trying below patch on 3.17 kernel. This is
>> tested and working on 3.4 kernel. I wanted to get some comments from
>> linux-serial mailing list.
>>
>> If UART_IER_RDI is enabled, for every quarter of fifo size bytes sent,
>> console_write() checks to see if there is data in receive fifo. If so,
>> it lets serial8250_rx_chars() pick those bytes and buffer them in tty
>> layer.
>
> The issue here occurs irrespective of whether a UART has a FIFO; it's
> even easier to get an overrun without a FIFO.
>
>> --- linux-3.17rc6-orig/linux-3.17-rc6/drivers/tty/serial/8250/8250_core.c
>>       2014-09-21 15:43:02.000000000 -0700
>> +++ linux-3.17-rc6/drivers/tty/serial/8250/8250_core.c  2014-09-30
>> 10:08:06.401242782 -0700
>> @@ -3004,6 +3004,7 @@
>>         unsigned long flags;
>>         unsigned int ier;
>>         int locked = 1;
>> +       int to_send, offset, fifo = count;
>>
>>         touch_nmi_watchdog();
>>
>> @@ -3022,8 +3023,27 @@
>>         else
>>                 serial_port_out(port, UART_IER, 0);
>>
>> -       uart_console_write(port, s, count, serial8250_console_putchar);
>> +       offset = 0;
>> +       if (uart_config[up->port.type].fcr & UART_FCR_ENABLE_FIFO) {
>> +               if (!oops_in_progress && ((ier & UART_IER_RDI) == UART_IER_RDI))
>> +                       fifo = port->fifosize >> 2;
>> +       }
>> +
>> +send_rest:
>> +       to_send = count > fifo ? fifo : count;
>>
>> +       uart_console_write(port, s + offset, to_send,
>> serial8250_console_putchar);
>> +
>> +       count -= to_send;
>> +       if (count > 0) {
>> +               unsigned int status;
>> +               status = serial_in(up, UART_LSR);
>> +               if (status & (UART_LSR_DR | UART_LSR_BI))
>> +                       serial8250_rx_chars(up, status);
>
> This will potentially take up to 256 chars in a loop, all with interrupts
> still off, and then drops the port lock which isn't ok in the context of
> a console message (with the way the driver works now).
>
> serial8250_rx_chars() doesn't need to drop the port lock anymore; so that
> solves that problem.
>
> Also, serial8250_rx_chars() is where sysrq and SAK handling is done;
> this would allow it to be re-entered, which would be bad. So rx cannot/
> should not be happening if either oops_in_progress or port->sysrq is true.
>
> I think a better way to do this would be to train wait_for_xmitr() to
> receive data to a temp buffer (if bits is set to the requisite LSR bits and
> not if oops_in_progress or port->sysrq). serial8250_rx_chars() would
> need refactoring to split out the per-char handling so it could work on
> the temp buffer once the console message was written.
>
> Regards,
> Peter Hurley
>
--
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