serial: amba-pl011: lock console writes against interrupts
authorRabin Vincent <rabin.vincent@stericsson.com>
Tue, 17 Jan 2012 10:52:28 +0000 (11:52 +0100)
committerGreg Kroah-Hartman <gregkh@suse.de>
Wed, 25 Jan 2012 00:09:55 +0000 (16:09 -0800)
Protect against pl011_console_write() and the interrupt for
the console UART running concurrently on different CPUs.

Otherwise the console_write could spin for a long time
waiting for the UART to become not busy, while the other
CPU continuously services UART interrupts and keeps the
UART busy.

The checks for sysrq and oops_in_progress are taken
from 8250.c.

Cc: stable <stable@vger.kernel.org>
Signed-off-by: Rabin Vincent <rabin.vincent@stericsson.com>
Reviewed-by: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com>
Reviewed-by: Bibek Basu <bibek.basu@stericsson.com>
Reviewed-by: Shreshtha Kumar Sahu <shreshthakumar.sahu@stericsson.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/tty/serial/amba-pl011.c

index ce843d058e02a1d9fc81e6f3f7a1b3989ce7ea0c..6800f5f26241430789a92efea30d58920dca9453 100644 (file)
@@ -1751,9 +1751,19 @@ pl011_console_write(struct console *co, const char *s, unsigned int count)
 {
        struct uart_amba_port *uap = amba_ports[co->index];
        unsigned int status, old_cr, new_cr;
+       unsigned long flags;
+       int locked = 1;
 
        clk_enable(uap->clk);
 
+       local_irq_save(flags);
+       if (uap->port.sysrq)
+               locked = 0;
+       else if (oops_in_progress)
+               locked = spin_trylock(&uap->port.lock);
+       else
+               spin_lock(&uap->port.lock);
+
        /*
         *      First save the CR then disable the interrupts
         */
@@ -1773,6 +1783,10 @@ pl011_console_write(struct console *co, const char *s, unsigned int count)
        } while (status & UART01x_FR_BUSY);
        writew(old_cr, uap->port.membase + UART011_CR);
 
+       if (locked)
+               spin_unlock(&uap->port.lock);
+       local_irq_restore(flags);
+
        clk_disable(uap->clk);
 }