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,

Thanks for reviewing the patch and reworking it. It looks good.

It makes sense to change wait_for_xmitr(). And the logic to call
serial8250_rx_chars() everytime only if !emergency_situation() is
good.

I can give this patch a try.

In your previous comment
(http://marc.info/?l=linux-serial&m=141900724429124&w=2), since I
initialized writing_to_console to 0, and is set to 1 only if certain
condition is true, I therefore did below code to reset that:

if (up->writing_to_con)
        up->writing_to_con = 0;

so as long as writing_to_con is not 0, it should go into that loop.
What is wrong with that? Another way would be to do "if
(up->writing_to_con != 0)" which may make compiler use 'cmp' and 'jne'
instructions.

>>>>>>>>>>>>>>>>>>>>
Two other, deeper issues with your patch:

1. It doesn't compile if SUPPORT_SYSRQ is not set.
2. The
spin_unlock(&port->lock);
tty_flip_buffer_push(&port->state->port);
spin_lock(&port->lock);
at the end of serial8250_rx_chars is risky during polled console
output.  To start with, serial8250_console_write doesn't necessarily
lock the port in the first place!  Fortunately, the condition
under which it might not (port->sysrq || oops_in_progress) is
exactly that during which input isn't processed.

Should the code instead just buffer to the flip_buffer and flip it when
returning from serial8250_console_write?

Looking at it, I'm not sure the spin_unlock is even required.
tty_flip_buffer_push(port)
-> tty_schedule_flip(port)
   -> schedule_work(work)
      -> queue_work(system_wq, work)
         -> queue_work_on(WORK_CPU_UNBOUND, system_wq, work)
            -> __queue_work()
      and I don't see anything that needs the port unlocked.

Anyway, my suggestion to pass the flag down is illustrated in the following
(untested) patch.  Also note the emergency_situation() function, which
ensures the logic is the same in the various locations.


diff --git a/drivers/tty/serial/8250/8250_core.c
b/drivers/tty/serial/8250/8250_core.c
index ca5cfdc..3a7af36 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -1416,9 +1416,13 @@ 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 if set 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;
@@ -1454,8 +1458,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)
@@ -1591,7 +1598,7 @@ int serial8250_handle_irq(struct uart_port
*port, unsigned int iir)
  dma_err = serial8250_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 && (status & UART_LSR_THRE))
@@ -1943,9 +1950,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 up->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;

@@ -1959,6 +1987,9 @@ 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);
  }

@@ -1970,12 +2001,24 @@ 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();
  }
  }
 }

+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
@@ -3175,7 +3218,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);
 }

@@ -3183,6 +3226,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
@@ -3198,7 +3245,7 @@ serial8250_console_write(struct console *co,
const char *s, unsigned int count)

  serial8250_rpm_get(up);

- if (port->sysrq || oops_in_progress)
+ if (emergency_situation(port))
  locked = spin_trylock_irqsave(&port->lock, flags);
  else
  spin_lock_irqsave(&port->lock, flags);
@@ -3219,7 +3266,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 3df10d5..8735c9c 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -131,7 +131,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 21c2e05..2739fac 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -389,7 +389,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;
--
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
[prev in list] [next in list] [prev in thread] [next in thread]


Configure | About | News | Add a list | Sponsored by KoreLogic

On Fri, Jan 9, 2015 at 2:37 PM, Greg KH <gregkh@xxxxxxxxxxxxxxxxxxx> wrote:
> On Wed, Jan 07, 2015 at 03:30:48PM -0800, Prasad Koya wrote:
>> Hi Greg
>>
>> Did you get any chance to look at below patch?
>>
>> [PATCH 1/1] tty, serial, 8250: to avoid recv fifo overrun, read rx
>> fifo during xmit
>
> I have processed all tty/serial patches in my queue now, if I missed
> something, please resend.
>
> thanks,
>
> greg k-h
--
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