Skip to content

Commit 853a9ae

Browse files
jhovoldgregkh
authored andcommitted
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: 75f4e83 ("serial: do not restore interrupt state in sysrq helper") Cc: [email protected] # 5.13 Cc: Joel Stanley <[email protected]> Cc: Andrew Jeffery <[email protected]> Reported-by: kernel test robot <[email protected]> Signed-off-by: Johan Hovold <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent cc9ca4d commit 853a9ae

File tree

4 files changed

+33
-6
lines changed

4 files changed

+33
-6
lines changed

drivers/tty/serial/8250/8250_aspeed_vuart.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,14 +329,15 @@ static int aspeed_vuart_handle_irq(struct uart_port *port)
329329
{
330330
struct uart_8250_port *up = up_to_u8250p(port);
331331
unsigned int iir, lsr;
332+
unsigned long flags;
332333
unsigned int space, count;
333334

334335
iir = serial_port_in(port, UART_IIR);
335336

336337
if (iir & UART_IIR_NO_INT)
337338
return 0;
338339

339-
spin_lock(&port->lock);
340+
spin_lock_irqsave(&port->lock, flags);
340341

341342
lsr = serial_port_in(port, UART_LSR);
342343

@@ -370,7 +371,7 @@ static int aspeed_vuart_handle_irq(struct uart_port *port)
370371
if (lsr & UART_LSR_THRE)
371372
serial8250_tx_chars(up);
372373

373-
uart_unlock_and_check_sysrq(port);
374+
uart_unlock_and_check_sysrq_irqrestore(port, flags);
374375

375376
return 1;
376377
}

drivers/tty/serial/8250/8250_fsl.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,11 @@ struct fsl8250_data {
3030
int fsl8250_handle_irq(struct uart_port *port)
3131
{
3232
unsigned char lsr, orig_lsr;
33+
unsigned long flags;
3334
unsigned int iir;
3435
struct uart_8250_port *up = up_to_u8250p(port);
3536

36-
spin_lock(&up->port.lock);
37+
spin_lock_irqsave(&up->port.lock, flags);
3738

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

8384
up->lsr_saved_flags = orig_lsr;
8485

85-
uart_unlock_and_check_sysrq(&up->port);
86+
uart_unlock_and_check_sysrq_irqrestore(&up->port, flags);
8687

8788
return 1;
8889
}

drivers/tty/serial/8250/8250_port.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1899,11 +1899,12 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
18991899
unsigned char status;
19001900
struct uart_8250_port *up = up_to_u8250p(port);
19011901
bool skip_rx = false;
1902+
unsigned long flags;
19021903

19031904
if (iir & UART_IIR_NO_INT)
19041905
return 0;
19051906

1906-
spin_lock(&port->lock);
1907+
spin_lock_irqsave(&port->lock, flags);
19071908

19081909
status = serial_port_in(port, UART_LSR);
19091910

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

1932-
uart_unlock_and_check_sysrq(port);
1933+
uart_unlock_and_check_sysrq_irqrestore(port, flags);
19331934

19341935
return 1;
19351936
}

include/linux/serial_core.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,25 @@ static inline void uart_unlock_and_check_sysrq(struct uart_port *port)
518518
if (sysrq_ch)
519519
handle_sysrq(sysrq_ch);
520520
}
521+
522+
static inline void uart_unlock_and_check_sysrq_irqrestore(struct uart_port *port,
523+
unsigned long flags)
524+
{
525+
int sysrq_ch;
526+
527+
if (!port->has_sysrq) {
528+
spin_unlock_irqrestore(&port->lock, flags);
529+
return;
530+
}
531+
532+
sysrq_ch = port->sysrq_ch;
533+
port->sysrq_ch = 0;
534+
535+
spin_unlock_irqrestore(&port->lock, flags);
536+
537+
if (sysrq_ch)
538+
handle_sysrq(sysrq_ch);
539+
}
521540
#else /* CONFIG_MAGIC_SYSRQ_SERIAL */
522541
static inline int uart_handle_sysrq_char(struct uart_port *port, unsigned int ch)
523542
{
@@ -531,6 +550,11 @@ static inline void uart_unlock_and_check_sysrq(struct uart_port *port)
531550
{
532551
spin_unlock(&port->lock);
533552
}
553+
static inline void uart_unlock_and_check_sysrq_irqrestore(struct uart_port *port,
554+
unsigned long flags)
555+
{
556+
spin_unlock_irqrestore(&port->lock, flags);
557+
}
534558
#endif /* CONFIG_MAGIC_SYSRQ_SERIAL */
535559

536560
/*

0 commit comments

Comments
 (0)