aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/serial/8250/8250_fsl.c
diff options
context:
space:
mode:
authorDarwin Dingel <darwin.dingel@alliedtelesis.co.nz>2018-12-10 11:29:09 +1300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-12-17 16:02:57 +0100
commit6d7f677a2afa1c82d7fc7af7f9159cbffd5dc010 (patch)
tree2b38e2d830923e12411591436627eed9aa89d339 /drivers/tty/serial/8250/8250_fsl.c
parentdt-bindings: serial: 8250: Add rate limit for serial port input overruns (diff)
downloadlinux-stable-6d7f677a2afa1c82d7fc7af7f9159cbffd5dc010.tar.xz
linux-stable-6d7f677a2afa1c82d7fc7af7f9159cbffd5dc010.zip
serial: 8250: Rate limit serial port rx interrupts during input overruns
When a serial port gets faulty or gets flooded with inputs, its interrupt handler starts to work double time to get the characters to the workqueue for the tty layer to handle them. When this busy time on the serial/tty subsystem happens during boot, where it is also busy on the userspace trying to initialise, some processes can continuously get preempted and will be on hold until the interrupts subside. The fix is to backoff on processing received characters for a specified amount of time when an input overrun is seen (received a new character before the previous one is processed). This only stops receive and will continue to transmit characters to serial port. After the backoff period is done, it receive will be re-enabled. This is optional and will only be enabled by setting 'overrun-throttle-ms' in the dts. Signed-off-by: Darwin Dingel <darwin.dingel@alliedtelesis.co.nz> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/serial/8250/8250_fsl.c')
-rw-r--r--drivers/tty/serial/8250/8250_fsl.c23
1 files changed, 22 insertions, 1 deletions
diff --git a/drivers/tty/serial/8250/8250_fsl.c b/drivers/tty/serial/8250/8250_fsl.c
index ff3dcaea5d93..aa0e216d5ead 100644
--- a/drivers/tty/serial/8250/8250_fsl.c
+++ b/drivers/tty/serial/8250/8250_fsl.c
@@ -49,8 +49,29 @@ 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))
+ /* Process incoming characters first */
+ if ((lsr & (UART_LSR_DR | UART_LSR_BI)) &&
+ (up->ier & (UART_IER_RLSI | UART_IER_RDI))) {
lsr = serial8250_rx_chars(up, lsr);
+ }
+
+ /* Stop processing interrupts on input overrun */
+ if ((orig_lsr & UART_LSR_OE) && (up->overrun_backoff_time_ms > 0)) {
+ unsigned long delay;
+
+ up->ier = port->serial_in(port, UART_IER);
+ if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) {
+ port->ops->stop_rx(port);
+ } else {
+ /* Keep restarting the timer until
+ * the input overrun subsides.
+ */
+ cancel_delayed_work(&up->overrun_backoff);
+ }
+
+ delay = msecs_to_jiffies(up->overrun_backoff_time_ms);
+ schedule_delayed_work(&up->overrun_backoff, delay);
+ }
serial8250_modem_status(up);