Commit 1cc1381e authored by Russell King's avatar Russell King

[SERIAL] Fix another SMP deadlock with modem status signal changes

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.
parent 94fad7de
......@@ -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
......
......@@ -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);
......@@ -73,27 +74,13 @@ 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 $");
......@@ -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"
......
......@@ -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");
......@@ -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) {
......
......@@ -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));
......@@ -103,27 +103,13 @@ __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");
......@@ -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);
......
......@@ -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");
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