Commit 853a9ae2 authored by Johan Hovold's avatar Johan Hovold Committed by Greg Kroah-Hartman

serial: 8250: fix handle_irq locking

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: 75f4e830 ("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: default avatarkernel test robot <oliver.sang@intel.com>
Signed-off-by: default avatarJohan Hovold <johan@kernel.org>
Link: https://lore.kernel.org/r/20210714080427.28164-1-johan@kernel.orgSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent cc9ca4d9
......@@ -329,6 +329,7 @@ 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;
unsigned int space, count;
iir = serial_port_in(port, UART_IIR);
......@@ -336,7 +337,7 @@ static int aspeed_vuart_handle_irq(struct uart_port *port)
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);
......@@ -370,7 +371,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;
}
......
......@@ -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) {
......@@ -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;
}
......
......@@ -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);
......@@ -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;
}
......
......@@ -518,6 +518,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)
{
......@@ -531,6 +550,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 */
/*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment