Skip to content

Commit

Permalink
serial: 8250: fix handle_irq locking
Browse files Browse the repository at this point in the history
commit 853a9ae upstream.

The 8250 handle_irq callback is not just called from the interrupt
handler but also from a timer callback when polling (e.g. for ports
without an interrupt line). Consequently the callback must explicitly
disable interrupts to avoid a potential deadlock with another interrupt
in polled mode.

Add back an irqrestore-version of the sysrq port-unlock helper and use
it in the 8250 callbacks that need it.

Fixes: 75f4e83 ("serial: do not restore interrupt state in sysrq helper")
Cc: stable@vger.kernel.org	# 5.13
Cc: Joel Stanley <joel@jms.id.au>
Cc: Andrew Jeffery <andrew@aj.id.au>
Reported-by: kernel test robot <oliver.sang@intel.com>
Signed-off-by: Johan Hovold <johan@kernel.org>
Link: https://lore.kernel.org/r/20210714080427.28164-1-johan@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
jhovold authored and gregkh committed Aug 12, 2021
1 parent 979bd0e commit b321bb8
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 6 deletions.
5 changes: 3 additions & 2 deletions drivers/tty/serial/8250/8250_aspeed_vuart.c
Expand Up @@ -320,14 +320,15 @@ static int aspeed_vuart_handle_irq(struct uart_port *port)
{
struct uart_8250_port *up = up_to_u8250p(port);
unsigned int iir, lsr;
unsigned long flags;
int space, count;

iir = serial_port_in(port, UART_IIR);

if (iir & UART_IIR_NO_INT)
return 0;

spin_lock(&port->lock);
spin_lock_irqsave(&port->lock, flags);

lsr = serial_port_in(port, UART_LSR);

Expand Down Expand Up @@ -363,7 +364,7 @@ static int aspeed_vuart_handle_irq(struct uart_port *port)
if (lsr & UART_LSR_THRE)
serial8250_tx_chars(up);

uart_unlock_and_check_sysrq(port);
uart_unlock_and_check_sysrq_irqrestore(port, flags);

return 1;
}
Expand Down
5 changes: 3 additions & 2 deletions drivers/tty/serial/8250/8250_fsl.c
Expand Up @@ -30,10 +30,11 @@ struct fsl8250_data {
int fsl8250_handle_irq(struct uart_port *port)
{
unsigned char lsr, orig_lsr;
unsigned long flags;
unsigned int iir;
struct uart_8250_port *up = up_to_u8250p(port);

spin_lock(&up->port.lock);
spin_lock_irqsave(&up->port.lock, flags);

iir = port->serial_in(port, UART_IIR);
if (iir & UART_IIR_NO_INT) {
Expand Down Expand Up @@ -82,7 +83,7 @@ int fsl8250_handle_irq(struct uart_port *port)

up->lsr_saved_flags = orig_lsr;

uart_unlock_and_check_sysrq(&up->port);
uart_unlock_and_check_sysrq_irqrestore(&up->port, flags);

return 1;
}
Expand Down
5 changes: 3 additions & 2 deletions drivers/tty/serial/8250/8250_port.c
Expand Up @@ -1899,11 +1899,12 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
unsigned char status;
struct uart_8250_port *up = up_to_u8250p(port);
bool skip_rx = false;
unsigned long flags;

if (iir & UART_IIR_NO_INT)
return 0;

spin_lock(&port->lock);
spin_lock_irqsave(&port->lock, flags);

status = serial_port_in(port, UART_LSR);

Expand All @@ -1929,7 +1930,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
(up->ier & UART_IER_THRI))
serial8250_tx_chars(up);

uart_unlock_and_check_sysrq(port);
uart_unlock_and_check_sysrq_irqrestore(port, flags);

return 1;
}
Expand Down
24 changes: 24 additions & 0 deletions include/linux/serial_core.h
Expand Up @@ -517,6 +517,25 @@ static inline void uart_unlock_and_check_sysrq(struct uart_port *port)
if (sysrq_ch)
handle_sysrq(sysrq_ch);
}

static inline void uart_unlock_and_check_sysrq_irqrestore(struct uart_port *port,
unsigned long flags)
{
int sysrq_ch;

if (!port->has_sysrq) {
spin_unlock_irqrestore(&port->lock, flags);
return;
}

sysrq_ch = port->sysrq_ch;
port->sysrq_ch = 0;

spin_unlock_irqrestore(&port->lock, flags);

if (sysrq_ch)
handle_sysrq(sysrq_ch);
}
#else /* CONFIG_MAGIC_SYSRQ_SERIAL */
static inline int uart_handle_sysrq_char(struct uart_port *port, unsigned int ch)
{
Expand All @@ -530,6 +549,11 @@ static inline void uart_unlock_and_check_sysrq(struct uart_port *port)
{
spin_unlock(&port->lock);
}
static inline void uart_unlock_and_check_sysrq_irqrestore(struct uart_port *port,
unsigned long flags)
{
spin_unlock_irqrestore(&port->lock, flags);
}
#endif /* CONFIG_MAGIC_SYSRQ_SERIAL */

/*
Expand Down

0 comments on commit b321bb8

Please sign in to comment.