[SERIAL] Fix another SMP deadlock with modem status signal changes
authorRussell King <rmk@flint.arm.linux.org.uk>
Tue, 23 Jul 2002 01:45:58 +0000 (02:45 +0100)
committerRussell King <rmk@flint.arm.linux.org.uk>
Tue, 23 Jul 2002 01:45:58 +0000 (02:45 +0100)
The original fix sent to Ingo for stop_tx didn't take account that
the start_tx and stop_tx methods can be called from the device
specific code under the port spinlock.  Consequently, we move the
spinlock to the callers of these methods.  Documentation updated
to reflect the change.

Documentation/serial/driver
drivers/serial/21285.c
drivers/serial/8250.c
drivers/serial/amba.c
drivers/serial/anakin.c
drivers/serial/clps711x.c
drivers/serial/core.c
drivers/serial/sa1100.c

index 7396d07..105e702 100644 (file)
@@ -3,7 +3,7 @@
                        --------------------
 
 
-   $Id: driver,v 1.9 2002/07/06 16:51:43 rmk Exp $
+   $Id: driver,v 1.10 2002/07/22 15:27:30 rmk Exp $
 
 
 This document is meant as a brief overview of some aspects of the new serial
@@ -119,7 +119,7 @@ hardware.
        tty_stop: 1 if this call is due to the TTY layer issuing a
                  TTY stop to the driver (equiv to rs_stop).
 
-       Locking: none.
+       Locking: port->lock taken.
        Interrupts: caller dependent.
        This call must not sleep
 
index 503bf31..369e6cc 100644 (file)
@@ -5,7 +5,7 @@
  *
  * Based on drivers/char/serial.c
  *
- *  $Id: 21285.c,v 1.33 2002/07/21 21:32:29 rmk Exp $
+ *  $Id: 21285.c,v 1.34 2002/07/22 15:27:32 rmk Exp $
  */
 #include <linux/config.h>
 #include <linux/module.h>
@@ -65,7 +65,8 @@ static const char serial21285_name[] = "Footbridge UART";
  *  int((BAUD_BASE - (baud >> 1)) / baud)
  */
 
-static void __serial21285_stop_tx(struct uart_port *port)
+static void
+serial21285_stop_tx(struct uart_port *port, unsigned int tty_stop)
 {
        if (tx_enabled(port)) {
                disable_irq(IRQ_CONTX);
@@ -74,26 +75,12 @@ static void __serial21285_stop_tx(struct uart_port *port)
 }
 
 static void
-serial21285_stop_tx(struct uart_port *port, unsigned int tty_stop)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&port->lock, flags);
-       __serial21285_stop_tx(port);
-       spin_unlock_irqrestore(&port->lock, flags);
-}
-
-static void
 serial21285_start_tx(struct uart_port *port, unsigned int tty_start)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&port->lock, flags);
        if (!tx_enabled(port)) {
                enable_irq(IRQ_CONTX);
                tx_enabled(port) = 1;
        }
-       spin_unlock_irqrestore(&port->lock, flags);
 }
 
 static void serial21285_stop_rx(struct uart_port *port)
@@ -185,7 +172,7 @@ static void serial21285_tx_chars(int irq, void *dev_id, struct pt_regs *regs)
                return;
        }
        if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
-               __serial21285_stop_tx(port);
+               serial21285_stop_tx(port, 0);
                return;
        }
 
@@ -201,7 +188,7 @@ static void serial21285_tx_chars(int irq, void *dev_id, struct pt_regs *regs)
                uart_event(port, EVT_WRITE_WAKEUP);
 
        if (uart_circ_empty(xmit))
-               __serial21285_stop_tx(port);
+               serial21285_stop_tx(port, 0);
 }
 
 static unsigned int serial21285_tx_empty(struct uart_port *port)
@@ -527,7 +514,7 @@ static int __init serial21285_init(void)
 {
        int ret;
 
-       printk(KERN_INFO "Serial: 21285 driver $Revision: 1.33 $\n");
+       printk(KERN_INFO "Serial: 21285 driver $Revision: 1.34 $\n");
 
        serial21285_setup_ports();
 
@@ -550,4 +537,4 @@ module_exit(serial21285_exit);
 EXPORT_NO_SYMBOLS;
 
 MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Intel Footbridge (21285) serial driver $Revision: 1.33 $");
+MODULE_DESCRIPTION("Intel Footbridge (21285) serial driver $Revision: 1.34 $");
index 5a9cf5c..5183621 100644 (file)
@@ -12,7 +12,7 @@
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
- *  $Id: 8250.c,v 1.83 2002/07/21 23:08:22 rmk Exp $
+ *  $Id: 8250.c,v 1.84 2002/07/22 15:27:32 rmk Exp $
  *
  * A note about mapbase / membase
  *
@@ -690,7 +690,7 @@ static void autoconfig_irq(struct uart_8250_port *up)
        up->port.irq = (irq > 0) ? irq : 0;
 }
 
-static void __serial8250_stop_tx(struct uart_port *port, unsigned int tty_stop)
+static void serial8250_stop_tx(struct uart_port *port, unsigned int tty_stop)
 {
        struct uart_8250_port *up = (struct uart_8250_port *)port;
 
@@ -704,16 +704,6 @@ static void __serial8250_stop_tx(struct uart_port *port, unsigned int tty_stop)
        }
 }
 
-static void serial8250_stop_tx(struct uart_port *port, unsigned int tty_stop)
-{
-       struct uart_8250_port *up = (struct uart_8250_port *)port;
-       unsigned long flags;
-
-       spin_lock_irqsave(&up->port.lock, flags);
-       __serial8250_stop_tx(port, tty_stop);
-       spin_unlock_irqrestore(&up->port.lock, flags);
-}
-
 static void serial8250_start_tx(struct uart_port *port, unsigned int tty_start)
 {
        struct uart_8250_port *up = (struct uart_8250_port *)port;
@@ -852,7 +842,7 @@ static _INLINE_ void transmit_chars(struct uart_8250_port *up)
                return;
        }
        if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
-               __serial8250_stop_tx(&up->port, 0);
+               serial8250_stop_tx(&up->port, 0);
                return;
        }
 
@@ -871,7 +861,7 @@ static _INLINE_ void transmit_chars(struct uart_8250_port *up)
        DEBUG_INTR("THRE...");
 
        if (uart_circ_empty(xmit))
-               __serial8250_stop_tx(&up->port, 0);
+               serial8250_stop_tx(&up->port, 0);
 }
 
 static _INLINE_ void check_modem_status(struct uart_8250_port *up)
@@ -1965,7 +1955,7 @@ static int __init serial8250_init(void)
 {
        int ret, i;
 
-       printk(KERN_INFO "Serial: 8250/16550 driver $Revision: 1.83 $ "
+       printk(KERN_INFO "Serial: 8250/16550 driver $Revision: 1.84 $ "
                "IRQ sharing %sabled\n", share_irqs ? "en" : "dis");
 
        for (i = 0; i < NR_IRQS; i++)
@@ -1997,7 +1987,7 @@ EXPORT_SYMBOL(unregister_serial);
 EXPORT_SYMBOL(serial8250_get_irq_map);
 
 MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Generic 8250/16x50 serial driver $Revision: 1.83 $");
+MODULE_DESCRIPTION("Generic 8250/16x50 serial driver $Revision: 1.84 $");
 
 MODULE_PARM(share_irqs, "i");
 MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices"
index a7bc7f4..c5d3157 100644 (file)
@@ -22,7 +22,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- *  $Id: amba.c,v 1.36 2002/07/21 21:32:30 rmk Exp $
+ *  $Id: amba.c,v 1.37 2002/07/22 15:27:32 rmk Exp $
  *
  * This is a generic driver for ARM AMBA-type serial ports.  They
  * have a lot of 16550-like features, but are not register compatable.
@@ -118,7 +118,7 @@ struct uart_amba_port {
        unsigned int            old_status;
 };
 
-static void __ambauart_stop_tx(struct uart_port *port)
+static void ambauart_stop_tx(struct uart_port *port, unsigned int tty_stop)
 {
        unsigned int cr;
 
@@ -127,15 +127,6 @@ static void __ambauart_stop_tx(struct uart_port *port)
        UART_PUT_CR(port, cr);
 }
 
-static void ambauart_stop_tx(struct uart_port *port, unsigned int tty_stop)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&port->lock, flags);
-       __ambauart_stop_tx(port);
-       spin_unlock_irqrestore(&port->lock, flags);
-}
-
 static void ambauart_start_tx(struct uart_port *port, unsigned int tty_start)
 {
        unsigned int cr;
@@ -261,7 +252,7 @@ static void ambauart_tx_chars(struct uart_port *port)
                return;
        }
        if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
-               __ambauart_stop_tx(port);
+               ambauart_stop_tx(port);
                return;
        }
 
@@ -278,7 +269,7 @@ static void ambauart_tx_chars(struct uart_port *port)
                uart_event(port, EVT_WRITE_WAKEUP);
 
        if (uart_circ_empty(xmit))
-               __ambauart_stop_tx(port);
+               ambauart_stop_tx(port);
 }
 
 static void ambauart_modem_status(struct uart_port *port)
@@ -751,7 +742,7 @@ static int __init ambauart_init(void)
 {
        int ret;
 
-       printk(KERN_INFO "Serial: AMBA driver $Revision: 1.36 $\n");
+       printk(KERN_INFO "Serial: AMBA driver $Revision: 1.37 $\n");
 
        ret = uart_register_driver(&amba_reg);
        if (ret == 0) {
@@ -779,5 +770,5 @@ module_exit(ambauart_exit);
 EXPORT_NO_SYMBOLS;
 
 MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd");
-MODULE_DESCRIPTION("ARM AMBA serial port driver $Revision: 1.36 $");
+MODULE_DESCRIPTION("ARM AMBA serial port driver $Revision: 1.37 $");
 MODULE_LICENSE("GPL");
index 22cd668..f01e383 100644 (file)
@@ -19,7 +19,7 @@
  *                     SA_INTERRUPT. Works reliably now. No longer requires
  *                     changes to the serial_core API.
  *
- *  $Id: anakin.c,v 1.28 2002/07/21 21:32:30 rmk Exp $
+ *  $Id: anakin.c,v 1.29 2002/07/22 15:27:32 rmk Exp $
  */
 
 #include <linux/config.h>
@@ -106,10 +106,6 @@ anakin_transmit_x_char(struct uart_port *port)
 static void
 anakin_start_tx(struct uart_port *port, unsigned int tty_start)
 {
-       unsigned int flags;
-
-       spin_lock_irqsave(&port->lock, flags);
-
        // is it this... or below
        if (!txenable[port->irq]) {
                txenable[port->irq] = TXENABLE;
@@ -118,8 +114,6 @@ anakin_start_tx(struct uart_port *port, unsigned int tty_start)
                    anakin_transmit_buffer(port);
                }
        }
-
-       spin_unlock_irqrestore(&port->lock, flags);
 }
 
 static void
@@ -524,7 +518,7 @@ anakin_init(void)
 {
        int ret;
 
-       printk(KERN_INFO "Serial: Anakin driver $Revision: 1.28 $\n");
+       printk(KERN_INFO "Serial: Anakin driver $Revision: 1.29 $\n");
 
        ret = uart_register_driver(&anakin_reg);
        if (ret == 0) {
index 1eb38d8..bc79f73 100644 (file)
@@ -22,7 +22,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- *  $Id: clps711x.c,v 1.39 2002/07/21 21:32:30 rmk Exp $
+ *  $Id: clps711x.c,v 1.40 2002/07/22 15:27:32 rmk Exp $
  *
  */
 #include <linux/config.h>
@@ -95,7 +95,7 @@
 #define tx_enabled(port)       ((port)->unused[0])
 
 static void
-__clps711xuart_stop_tx(struct uart_port *port)
+clps711xuart_stop_tx(struct uart_port *port, unsigned int tty_stop)
 {
        if (tx_enabled(port)) {
                disable_irq(TX_IRQ(port));
@@ -104,26 +104,12 @@ __clps711xuart_stop_tx(struct uart_port *port)
 }
 
 static void
-clps711xuart_stop_tx(struct uart_port *port, unsigned int tty_stop)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&port->lock, flags);
-       __clps711xuart_stop_tx(port);
-       spin_unlock_irqrestore(&port->lock, flags);
-}
-
-static void
 clps711xuart_start_tx(struct uart_port *port, unsigned int tty_start)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&port->lock, flags);
        if (!tx_enabled(port)) {
                enable_irq(TX_IRQ(port));
                tx_enabled(port) = 1;
        }
-       spin_unlock_irqrestore(&port->lock, flags);
 }
 
 static void clps711xuart_stop_rx(struct uart_port *port)
@@ -224,7 +210,7 @@ static void clps711xuart_int_tx(int irq, void *dev_id, struct pt_regs *regs)
                return;
        }
        if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
-               __clps711xuart_stop_tx(port);
+               clps711xuart_stop_tx(port);
                return;
        }
 
@@ -241,7 +227,7 @@ static void clps711xuart_int_tx(int irq, void *dev_id, struct pt_regs *regs)
                uart_event(port, EVT_WRITE_WAKEUP);
 
        if (uart_circ_empty(xmit))
-               __clps711xuart_stop_tx(port);
+               clps711xuart_stop_tx(port);
 }
 
 static unsigned int clps711xuart_tx_empty(struct uart_port *port)
@@ -611,7 +597,7 @@ static int __init clps711xuart_init(void)
 {
        int ret, i;
 
-       printk(KERN_INFO "Serial: CLPS711x driver $Revision: 1.39 $\n");
+       printk(KERN_INFO "Serial: CLPS711x driver $Revision: 1.40 $\n");
 
        ret = uart_register_driver(&clps711x_reg);
        if (ret)
@@ -639,5 +625,5 @@ module_exit(clps711xuart_exit);
 EXPORT_NO_SYMBOLS;
 
 MODULE_AUTHOR("Deep Blue Solutions Ltd");
-MODULE_DESCRIPTION("CLPS-711x generic serial driver $Revision: 1.39 $");
+MODULE_DESCRIPTION("CLPS-711x generic serial driver $Revision: 1.40 $");
 MODULE_LICENSE("GPL");
index 5593655..724ad65 100644 (file)
@@ -22,7 +22,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- *  $Id: core.c,v 1.90 2002/07/21 21:32:30 rmk Exp $
+ *  $Id: core.c,v 1.91 2002/07/22 15:27:32 rmk Exp $
  *
  */
 #include <linux/config.h>
@@ -93,18 +93,24 @@ static void uart_stop(struct tty_struct *tty)
 {
        struct uart_info *info = tty->driver_data;
        struct uart_port *port = info->port;
+       unsigned long flags;
 
+       spin_lock_irqsave(&port->lock, flags);
        port->ops->stop_tx(port, 1);
+       spin_unlock_irqrestore(&port->lock, flags);
 }
 
 static void __uart_start(struct tty_struct *tty)
 {
        struct uart_info *info = tty->driver_data;
        struct uart_port *port = info->port;
+       unsigned long flags;
 
+       spin_lock_irqsave(&port->lock, flags);
        if (!uart_circ_empty(&info->xmit) && info->xmit.buf &&
            !tty->stopped && !tty->hw_stopped)
                port->ops->start_tx(port, 1);
+       spin_unlock_irqrestore(&port->flags, flags);
 }
 
 static void uart_start(struct tty_struct *tty)
@@ -554,13 +560,17 @@ static void uart_send_xchar(struct tty_struct *tty, char ch)
 {
        struct uart_info *info = tty->driver_data;
        struct uart_port *port = info->port;
+       unsigned long flags;
 
        if (port->ops->send_xchar)
                port->ops->send_xchar(port, ch);
        else {
                port->x_char = ch;
-               if (ch)
+               if (ch) {
+                       spin_lock_irqsave(&port->lock, flags);
                        port->ops->start_tx(port, 0);
+                       spin_unlock_irqrestore(&port->lock, flags);
+               }
        }
 }
 
@@ -1935,8 +1945,8 @@ static int uart_pm_set_state(struct uart_state *state, int pm_state, int oldstat
                        port->cons->flags &= ~CON_ENABLED;
 
                if (running) {
-                       ops->stop_tx(port, 0);
                        spin_lock_irq(&port->lock);
+                       ops->stop_tx(port, 0);
                        ops->set_mctrl(port, 0);
                        spin_unlock_irq(&port->lock);
                        ops->stop_rx(port);
index 45fe87b..5cb10a4 100644 (file)
@@ -21,7 +21,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- *  $Id: sa1100.c,v 1.42 2002/07/21 21:32:30 rmk Exp $
+ *  $Id: sa1100.c,v 1.43 2002/07/22 15:27:32 rmk Exp $
  *
  */
 #include <linux/config.h>
@@ -155,26 +155,17 @@ static void sa1100_timeout(unsigned long data)
        }
 }
 
-static void __sa1100_stop_tx(struct sa1100_port *sport)
-{
-       u32 utcr3;
-
-       utcr3 = UART_GET_UTCR3(sport);
-       UART_PUT_UTCR3(sport, utcr3 & ~UTCR3_TIE);
-       sport->port.read_status_mask &= ~UTSR0_TO_SM(UTSR0_TFS);
-}
-
 /*
  * interrupts disabled on entry
  */
 static void sa1100_stop_tx(struct uart_port *port, unsigned int tty_stop)
 {
        struct sa1100_port *sport = (struct sa1100_port *)port;
-       unsigned long flags;
+       u32 utcr3;
 
-       spin_lock_irqsave(&sport->port.lock, flags);
-       __sa1100_stop_tx(sport);
-       spin_unlock_irqrestore(&sport->port.lock, flags);
+       utcr3 = UART_GET_UTCR3(sport);
+       UART_PUT_UTCR3(sport, utcr3 & ~UTCR3_TIE);
+       sport->port.read_status_mask &= ~UTSR0_TO_SM(UTSR0_TFS);
 }
 
 /*
@@ -315,7 +306,7 @@ static void sa1100_tx_chars(struct sa1100_port *sport)
        sa1100_mctrl_check(sport);
 
        if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
-               __sa1100_stop_tx(sport);
+               sa1100_stop_tx(sport);
                return;
        }
 
@@ -335,7 +326,7 @@ static void sa1100_tx_chars(struct sa1100_port *sport)
                uart_event(&sport->port, EVT_WRITE_WAKEUP);
 
        if (uart_circ_empty(xmit))
-               __sa1100_stop_tx(sport);
+               sa1100_stop_tx(sport);
 }
 
 static void sa1100_int(int irq, void *dev_id, struct pt_regs *regs)
@@ -866,7 +857,7 @@ static int __init sa1100_serial_init(void)
 {
        int ret;
 
-       printk(KERN_INFO "Serial: SA11x0 driver $Revision: 1.42 $\n");
+       printk(KERN_INFO "Serial: SA11x0 driver $Revision: 1.43 $\n");
 
        sa1100_init_ports();
        ret = uart_register_driver(&sa1100_reg);
@@ -895,5 +886,5 @@ module_exit(sa1100_serial_exit);
 EXPORT_NO_SYMBOLS;
 
 MODULE_AUTHOR("Deep Blue Solutions Ltd");
-MODULE_DESCRIPTION("SA1100 generic serial port driver $Revision: 1.42 $");
+MODULE_DESCRIPTION("SA1100 generic serial port driver $Revision: 1.43 $");
 MODULE_LICENSE("GPL");