Commit 9a23a1d1 authored by Taichi Kageyama's avatar Taichi Kageyama Committed by Greg Kroah-Hartman

serial: 8250: Fix autoconfig_irq() to avoid race conditions

The following race conditions can happen when a serial port is used
as console.

Case1: CPU_B is used to detect an interrupt from a serial port,
       but it can have interrupts disabled during the waiting time.
Case2: CPU_B clears UART_IER just after CPU_A sets UART_IER and then
       a serial port may not make an interrupt.
Case3: CPU_A sets UART_IER just after CPU_B clears UART_IER.
       This is an unexpected behavior for serial8250_console_write().

CPU_A [autoconfig_irq]      |  CPU_B [serial8250_console_write]
----------------------------|---------------------------------------
                            |
probe_irq_on()              |  spin_lock_irqsave(&port->lock,)
serial_outp(,UART_IER,0x0f) |  serial_out(,UART_IER,0)
udelay(20);                 |  uart_console_write()
probe_irq_off()             |
                            |  spin_unlock_irqrestore(&port->lock,)

Case1 and 2 can make autoconfig_irq() failed.
In these cases, the console doesn't work in interrupt mode and
"input overrun" (which can make operation mistakes) can happen
on some systems. Especially in the Case1, It is known that the
problem happens with high rate every boot once it occurs
because the boot sequence is always almost same.

port mutex makes sure that the autoconfig operation is exclusive of
any other concurrent HW access except by the console operation.
console lock is required in autoconfig_irq().
Signed-off-by: default avatarTaichi Kageyama <t-kageyama@cp.jp.nec.com>
Cc: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Reviewed-by: default avatarPeter Hurley <peter@hurleysoftware.com>
Reviewed-by: default avatarThomas Gleixner <tglx@linutronix.de>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent e4b4e317
...@@ -1238,6 +1238,9 @@ static void autoconfig_irq(struct uart_8250_port *up) ...@@ -1238,6 +1238,9 @@ static void autoconfig_irq(struct uart_8250_port *up)
inb_p(ICP); inb_p(ICP);
} }
if (uart_console(port))
console_lock();
/* forget possible initially masked and pending IRQ */ /* forget possible initially masked and pending IRQ */
probe_irq_off(probe_irq_on()); probe_irq_off(probe_irq_on());
save_mcr = serial_in(up, UART_MCR); save_mcr = serial_in(up, UART_MCR);
...@@ -1269,6 +1272,9 @@ static void autoconfig_irq(struct uart_8250_port *up) ...@@ -1269,6 +1272,9 @@ static void autoconfig_irq(struct uart_8250_port *up)
if (port->flags & UPF_FOURPORT) if (port->flags & UPF_FOURPORT)
outb_p(save_ICP, ICP); outb_p(save_ICP, ICP);
if (uart_console(port))
console_unlock();
port->irq = (irq > 0) ? irq : 0; port->irq = (irq > 0) ? irq : 0;
} }
......
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