Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Darwin Dingel and committed by
Greg Kroah-Hartman
6d7f677a bdb48e4c

+56 -1
+25
drivers/tty/serial/8250/8250_core.c
··· 942 942 return NULL; 943 943 } 944 944 945 + static void serial_8250_overrun_backoff_work(struct work_struct *work) 946 + { 947 + struct uart_8250_port *up = 948 + container_of(to_delayed_work(work), struct uart_8250_port, 949 + overrun_backoff); 950 + struct uart_port *port = &up->port; 951 + unsigned long flags; 952 + 953 + spin_lock_irqsave(&port->lock, flags); 954 + up->ier |= UART_IER_RLSI | UART_IER_RDI; 955 + up->port.read_status_mask |= UART_LSR_DR; 956 + serial_out(up, UART_IER, up->ier); 957 + spin_unlock_irqrestore(&port->lock, flags); 958 + } 959 + 945 960 /** 946 961 * serial8250_register_8250_port - register a serial port 947 962 * @up: serial port template ··· 1071 1056 ret = 0; 1072 1057 } 1073 1058 } 1059 + 1060 + /* Initialise interrupt backoff work if required */ 1061 + if (up->overrun_backoff_time_ms > 0) { 1062 + uart->overrun_backoff_time_ms = up->overrun_backoff_time_ms; 1063 + INIT_DELAYED_WORK(&uart->overrun_backoff, 1064 + serial_8250_overrun_backoff_work); 1065 + } else { 1066 + uart->overrun_backoff_time_ms = 0; 1067 + } 1068 + 1074 1069 mutex_unlock(&serial_mutex); 1075 1070 1076 1071 return ret;
+22 -1
drivers/tty/serial/8250/8250_fsl.c
··· 49 49 50 50 lsr = orig_lsr = up->port.serial_in(&up->port, UART_LSR); 51 51 52 - if (lsr & (UART_LSR_DR | UART_LSR_BI)) 52 + /* Process incoming characters first */ 53 + if ((lsr & (UART_LSR_DR | UART_LSR_BI)) && 54 + (up->ier & (UART_IER_RLSI | UART_IER_RDI))) { 53 55 lsr = serial8250_rx_chars(up, lsr); 56 + } 57 + 58 + /* Stop processing interrupts on input overrun */ 59 + if ((orig_lsr & UART_LSR_OE) && (up->overrun_backoff_time_ms > 0)) { 60 + unsigned long delay; 61 + 62 + up->ier = port->serial_in(port, UART_IER); 63 + if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) { 64 + port->ops->stop_rx(port); 65 + } else { 66 + /* Keep restarting the timer until 67 + * the input overrun subsides. 68 + */ 69 + cancel_delayed_work(&up->overrun_backoff); 70 + } 71 + 72 + delay = msecs_to_jiffies(up->overrun_backoff_time_ms); 73 + schedule_delayed_work(&up->overrun_backoff, delay); 74 + } 54 75 55 76 serial8250_modem_status(up); 56 77
+5
drivers/tty/serial/8250/8250_of.c
··· 240 240 if (of_property_read_bool(ofdev->dev.of_node, "auto-flow-control")) 241 241 port8250.capabilities |= UART_CAP_AFE; 242 242 243 + if (of_property_read_u32(ofdev->dev.of_node, 244 + "overrun-throttle-ms", 245 + &port8250.overrun_backoff_time_ms) != 0) 246 + port8250.overrun_backoff_time_ms = 0; 247 + 243 248 ret = serial8250_register_8250_port(&port8250); 244 249 if (ret < 0) 245 250 goto err_dispose;
+4
include/linux/serial_8250.h
··· 134 134 void (*dl_write)(struct uart_8250_port *, int); 135 135 136 136 struct uart_8250_em485 *em485; 137 + 138 + /* Serial port overrun backoff */ 139 + struct delayed_work overrun_backoff; 140 + u32 overrun_backoff_time_ms; 137 141 }; 138 142 139 143 static inline struct uart_8250_port *up_to_u8250p(struct uart_port *up)