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: 75f4e830fa
("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>
This commit is contained in:
parent
cc9ca4d958
commit
853a9ae29e
|
@ -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 */
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue