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) ...@@ -329,6 +329,7 @@ static int aspeed_vuart_handle_irq(struct uart_port *port)
{ {
struct uart_8250_port *up = up_to_u8250p(port); struct uart_8250_port *up = up_to_u8250p(port);
unsigned int iir, lsr; unsigned int iir, lsr;
unsigned long flags;
unsigned int space, count; unsigned int space, count;
iir = serial_port_in(port, UART_IIR); iir = serial_port_in(port, UART_IIR);
...@@ -336,7 +337,7 @@ static int aspeed_vuart_handle_irq(struct uart_port *port) ...@@ -336,7 +337,7 @@ static int aspeed_vuart_handle_irq(struct uart_port *port)
if (iir & UART_IIR_NO_INT) if (iir & UART_IIR_NO_INT)
return 0; return 0;
spin_lock(&port->lock); spin_lock_irqsave(&port->lock, flags);
lsr = serial_port_in(port, UART_LSR); lsr = serial_port_in(port, UART_LSR);
...@@ -370,7 +371,7 @@ static int aspeed_vuart_handle_irq(struct uart_port *port) ...@@ -370,7 +371,7 @@ static int aspeed_vuart_handle_irq(struct uart_port *port)
if (lsr & UART_LSR_THRE) if (lsr & UART_LSR_THRE)
serial8250_tx_chars(up); serial8250_tx_chars(up);
uart_unlock_and_check_sysrq(port); uart_unlock_and_check_sysrq_irqrestore(port, flags);
return 1; return 1;
} }
......
...@@ -30,10 +30,11 @@ struct fsl8250_data { ...@@ -30,10 +30,11 @@ struct fsl8250_data {
int fsl8250_handle_irq(struct uart_port *port) int fsl8250_handle_irq(struct uart_port *port)
{ {
unsigned char lsr, orig_lsr; unsigned char lsr, orig_lsr;
unsigned long flags;
unsigned int iir; unsigned int iir;
struct uart_8250_port *up = up_to_u8250p(port); 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); iir = port->serial_in(port, UART_IIR);
if (iir & UART_IIR_NO_INT) { if (iir & UART_IIR_NO_INT) {
...@@ -82,7 +83,7 @@ int fsl8250_handle_irq(struct uart_port *port) ...@@ -82,7 +83,7 @@ int fsl8250_handle_irq(struct uart_port *port)
up->lsr_saved_flags = orig_lsr; up->lsr_saved_flags = orig_lsr;
uart_unlock_and_check_sysrq(&up->port); uart_unlock_and_check_sysrq_irqrestore(&up->port, flags);
return 1; return 1;
} }
......
...@@ -1899,11 +1899,12 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) ...@@ -1899,11 +1899,12 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
unsigned char status; unsigned char status;
struct uart_8250_port *up = up_to_u8250p(port); struct uart_8250_port *up = up_to_u8250p(port);
bool skip_rx = false; bool skip_rx = false;
unsigned long flags;
if (iir & UART_IIR_NO_INT) if (iir & UART_IIR_NO_INT)
return 0; return 0;
spin_lock(&port->lock); spin_lock_irqsave(&port->lock, flags);
status = serial_port_in(port, UART_LSR); status = serial_port_in(port, UART_LSR);
...@@ -1929,7 +1930,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) ...@@ -1929,7 +1930,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
(up->ier & UART_IER_THRI)) (up->ier & UART_IER_THRI))
serial8250_tx_chars(up); serial8250_tx_chars(up);
uart_unlock_and_check_sysrq(port); uart_unlock_and_check_sysrq_irqrestore(port, flags);
return 1; return 1;
} }
......
...@@ -518,6 +518,25 @@ static inline void uart_unlock_and_check_sysrq(struct uart_port *port) ...@@ -518,6 +518,25 @@ static inline void uart_unlock_and_check_sysrq(struct uart_port *port)
if (sysrq_ch) if (sysrq_ch)
handle_sysrq(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 */ #else /* CONFIG_MAGIC_SYSRQ_SERIAL */
static inline int uart_handle_sysrq_char(struct uart_port *port, unsigned int ch) 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) ...@@ -531,6 +550,11 @@ static inline void uart_unlock_and_check_sysrq(struct uart_port *port)
{ {
spin_unlock(&port->lock); 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 */ #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