Re: [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. Could someone review this patch?

Thank you.

On Tue, Dec 9, 2014 at 11:34 PM, Prasad Koya <prasad.koya@xxxxxxxxx> wrote:
> 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