Re: [PATCH] serial: 8250: Rate limit serial port rx interrupts during input overruns

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

 



On Tuesday 11 September 2018 08:35 AM, Darwin Dingel wrote:
> 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).

You must really be using HW flow control to avoid HW FIFO overrruns.

> 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.
> 

Is this binding documented? If not, this new binding should be
documented under Documentation/devicetree/bindings/serial/serial.txt and
with DT maintainer's ACK

Also, please CC maintainers listed by running
./scripts/get_maintainer.pl on the patches you submit. Else, you may not
get their attention quickly.

Regards
Vignesh

> Reviewed-by: Andy Shevchenko <andriy.shevchenko@xxxxxxxxxxxxxxx>
> Signed-off-by: Darwin Dingel <darwin.dingel@xxxxxxxxxxxxxxxxxxx>
> ---
>  drivers/tty/serial/8250/8250_core.c | 25 +++++++++++++++++++++++++
>  drivers/tty/serial/8250/8250_fsl.c  | 23 ++++++++++++++++++++++-
>  drivers/tty/serial/8250/8250_of.c   |  5 +++++
>  include/linux/serial_8250.h         |  4 ++++
>  4 files changed, 56 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
> index 8fe3d0ed229e..0e65d4261f94 100644
> --- a/drivers/tty/serial/8250/8250_core.c
> +++ b/drivers/tty/serial/8250/8250_core.c
> @@ -946,6 +946,21 @@ static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port *
>  	return NULL;
>  }
>  
> +static void serial_8250_overrun_backoff_work(struct work_struct *work)
> +{
> +	struct uart_8250_port *up =
> +	    container_of(to_delayed_work(work), struct uart_8250_port,
> +			 overrun_backoff);
> +	struct uart_port *port = &up->port;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&port->lock, flags);
> +	up->ier |= UART_IER_RLSI | UART_IER_RDI;
> +	up->port.read_status_mask |= UART_LSR_DR;
> +	serial_out(up, UART_IER, up->ier);
> +	spin_unlock_irqrestore(&port->lock, flags);
> +}
> +
>  /**
>   *	serial8250_register_8250_port - register a serial port
>   *	@up: serial port template
> @@ -1060,6 +1075,16 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
>  			ret = 0;
>  		}
>  	}
> +
> +	/* Initialise interrupt backoff work if required */
> +	if (up->overrun_backoff_time_ms > 0) {
> +		uart->overrun_backoff_time_ms = up->overrun_backoff_time_ms;
> +		INIT_DELAYED_WORK(&uart->overrun_backoff,
> +				  serial_8250_overrun_backoff_work);
> +	} else {
> +		uart->overrun_backoff_time_ms = 0;
> +	}
> +
>  	mutex_unlock(&serial_mutex);
>  
>  	return ret;
> diff --git a/drivers/tty/serial/8250/8250_fsl.c b/drivers/tty/serial/8250/8250_fsl.c
> index 6640a4c7ddd1..bb9571eed275 100644
> --- a/drivers/tty/serial/8250/8250_fsl.c
> +++ b/drivers/tty/serial/8250/8250_fsl.c
> @@ -45,8 +45,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);
>  
> diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c
> index af8beefe9b5c..2a3244067ed6 100644
> --- a/drivers/tty/serial/8250/8250_of.c
> +++ b/drivers/tty/serial/8250/8250_of.c
> @@ -236,6 +236,11 @@ static int of_platform_serial_probe(struct platform_device *ofdev)
>  	if (of_property_read_bool(ofdev->dev.of_node, "auto-flow-control"))
>  		port8250.capabilities |= UART_CAP_AFE;
>  
> +	if (of_property_read_u32(ofdev->dev.of_node,
> +			"overrun-throttle-ms",
> +			&port8250.overrun_backoff_time_ms) != 0)
> +		port8250.overrun_backoff_time_ms = 0;
> +
>  	ret = serial8250_register_8250_port(&port8250);
>  	if (ret < 0)
>  		goto err_dispose;
> diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
> index 18e21427bce4..5a655ba8d273 100644
> --- a/include/linux/serial_8250.h
> +++ b/include/linux/serial_8250.h
> @@ -134,6 +134,10 @@ struct uart_8250_port {
>  	void			(*dl_write)(struct uart_8250_port *, int);
>  
>  	struct uart_8250_em485 *em485;
> +
> +	/* Serial port overrun backoff */
> +	struct delayed_work overrun_backoff;
> +	u32 overrun_backoff_time_ms;
>  };
>  
>  static inline struct uart_8250_port *up_to_u8250p(struct uart_port *up)
> 



[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