[SERIAL]: Fix console write locking in sparc drivers.
authorDavid S. Miller <davem@sunset.davemloft.net>
Mon, 16 Jul 2007 06:53:32 +0000 (23:53 -0700)
committerDavid S. Miller <davem@sunset.davemloft.net>
Mon, 16 Jul 2007 11:05:28 +0000 (04:05 -0700)
Mirror the logic in 8250 for proper console write locking
when SYSRQ is triggered or an OOPS is in progress.

Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/serial/sunhv.c
drivers/serial/sunsab.c
drivers/serial/sunsu.c
drivers/serial/sunzilog.c

index 96557e6dba60c4d57a1d96842ba5aa6cd3e8902b..17bcca53d6a1a70fb5efc374c876160961facb91 100644 (file)
@@ -440,8 +440,16 @@ static void sunhv_console_write_paged(struct console *con, const char *s, unsign
 {
        struct uart_port *port = sunhv_port;
        unsigned long flags;
+       int locked = 1;
+
+       local_irq_save(flags);
+       if (port->sysrq) {
+               locked = 0;
+       } else if (oops_in_progress) {
+               locked = spin_trylock(&port->lock);
+       } else
+               spin_lock(&port->lock);
 
-       spin_lock_irqsave(&port->lock, flags);
        while (n > 0) {
                unsigned long ra = __pa(con_write_page);
                unsigned long page_bytes;
@@ -469,7 +477,10 @@ static void sunhv_console_write_paged(struct console *con, const char *s, unsign
                        ra += written;
                }
        }
-       spin_unlock_irqrestore(&port->lock, flags);
+
+       if (locked)
+               spin_unlock(&port->lock);
+       local_irq_restore(flags);
 }
 
 static inline void sunhv_console_putchar(struct uart_port *port, char c)
@@ -488,7 +499,15 @@ static void sunhv_console_write_bychar(struct console *con, const char *s, unsig
 {
        struct uart_port *port = sunhv_port;
        unsigned long flags;
-       int i;
+       int i, locked = 1;
+
+       local_irq_save(flags);
+       if (port->sysrq) {
+               locked = 0;
+       } else if (oops_in_progress) {
+               locked = spin_trylock(&port->lock);
+       } else
+               spin_lock(&port->lock);
 
        spin_lock_irqsave(&port->lock, flags);
        for (i = 0; i < n; i++) {
@@ -496,7 +515,10 @@ static void sunhv_console_write_bychar(struct console *con, const char *s, unsig
                        sunhv_console_putchar(port, '\r');
                sunhv_console_putchar(port, *s++);
        }
-       spin_unlock_irqrestore(&port->lock, flags);
+
+       if (locked)
+               spin_unlock(&port->lock);
+       local_irq_restore(flags);
 }
 
 static struct console sunhv_console = {
index deb9ab4b5a0b6b53c3167c842a833e71d4fcc4fe..8a0f9e4408d4a78976040178885efc1571b7a184 100644 (file)
@@ -860,22 +860,31 @@ static int num_channels;
 static void sunsab_console_putchar(struct uart_port *port, int c)
 {
        struct uart_sunsab_port *up = (struct uart_sunsab_port *)port;
-       unsigned long flags;
-
-       spin_lock_irqsave(&up->port.lock, flags);
 
        sunsab_tec_wait(up);
        writeb(c, &up->regs->w.tic);
-
-       spin_unlock_irqrestore(&up->port.lock, flags);
 }
 
 static void sunsab_console_write(struct console *con, const char *s, unsigned n)
 {
        struct uart_sunsab_port *up = &sunsab_ports[con->index];
+       unsigned long flags;
+       int locked = 1;
+
+       local_irq_save(flags);
+       if (up->port.sysrq) {
+               locked = 0;
+       } else if (oops_in_progress) {
+               locked = spin_trylock(&up->port.lock);
+       } else
+               spin_lock(&up->port.lock);
 
        uart_console_write(&up->port, s, n, sunsab_console_putchar);
        sunsab_tec_wait(up);
+
+       if (locked)
+               spin_unlock(&up->port.lock);
+       local_irq_restore(flags);
 }
 
 static int sunsab_console_setup(struct console *con, char *options)
index 2a63cdba32083eae3984578722ba7159fc58eb06..26d720baf88c17619577d09420b50cade271e09d 100644 (file)
@@ -1288,7 +1288,17 @@ static void sunsu_console_write(struct console *co, const char *s,
                                unsigned int count)
 {
        struct uart_sunsu_port *up = &sunsu_ports[co->index];
+       unsigned long flags;
        unsigned int ier;
+       int locked = 1;
+
+       local_irq_save(flags);
+       if (up->port.sysrq) {
+               locked = 0;
+       } else if (oops_in_progress) {
+               locked = spin_trylock(&up->port.lock);
+       } else
+               spin_lock(&up->port.lock);
 
        /*
         *      First save the UER then disable the interrupts
@@ -1304,6 +1314,10 @@ static void sunsu_console_write(struct console *co, const char *s,
         */
        wait_for_xmitr(up);
        serial_out(up, UART_IER, ier);
+
+       if (locked)
+               spin_unlock(&up->port.lock);
+       local_irq_restore(flags);
 }
 
 /*
index 15b6e1cb040be54994ea9cbeea68bce51a61e2bf..0a3e10a4a35dec59485e1455b5ee8f30a0277c25 100644 (file)
@@ -9,7 +9,7 @@
  * C. Dost, Pete Zaitcev, Ted Ts'o and Alex Buell for their
  * work there.
  *
- *  Copyright (C) 2002, 2006 David S. Miller (davem@davemloft.net)
+ * Copyright (C) 2002, 2006, 2007 David S. Miller (davem@davemloft.net)
  */
 
 #include <linux/module.h>
@@ -1151,11 +1151,22 @@ sunzilog_console_write(struct console *con, const char *s, unsigned int count)
 {
        struct uart_sunzilog_port *up = &sunzilog_port_table[con->index];
        unsigned long flags;
+       int locked = 1;
+
+       local_irq_save(flags);
+       if (up->port.sysrq) {
+               locked = 0;
+       } else if (oops_in_progress) {
+               locked = spin_trylock(&up->port.lock);
+       } else
+               spin_lock(&up->port.lock);
 
-       spin_lock_irqsave(&up->port.lock, flags);
        uart_console_write(&up->port, s, count, sunzilog_putchar);
        udelay(2);
-       spin_unlock_irqrestore(&up->port.lock, flags);
+
+       if (locked)
+               spin_unlock(&up->port.lock);
+       local_irq_restore(flags);
 }
 
 static int __init sunzilog_console_setup(struct console *con, char *options)