debug lockups: Improve lockup detection, fix generic arch fallback
[pandora-kernel.git] / drivers / char / generic_serial.c
index c6090f8..9e4e569 100644 (file)
@@ -376,7 +376,8 @@ static void gs_shutdown_port (struct gs_port *port)
 
 void gs_hangup(struct tty_struct *tty)
 {
-       struct gs_port   *port;
+       struct gs_port *port;
+       unsigned long flags;
 
        func_enter ();
 
@@ -386,9 +387,11 @@ void gs_hangup(struct tty_struct *tty)
                return;
 
        gs_shutdown_port (port);
+       spin_lock_irqsave(&port->port.lock, flags);
        port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|GS_ACTIVE);
        port->port.tty = NULL;
        port->port.count = 0;
+       spin_unlock_irqrestore(&port->port.lock, flags);
 
        wake_up_interruptible(&port->port.open_wait);
        func_exit ();
@@ -397,7 +400,8 @@ void gs_hangup(struct tty_struct *tty)
 
 int gs_block_til_ready(void *port_, struct file * filp)
 {
-       struct gs_port *port = port_;
+       struct gs_port *gp = port_;
+       struct tty_port *port = &gp->port;
        DECLARE_WAITQUEUE(wait, current);
        int    retval;
        int    do_clocal = 0;
@@ -409,16 +413,16 @@ int gs_block_til_ready(void *port_, struct file * filp)
 
        if (!port) return 0;
 
-       tty = port->port.tty;
+       tty = port->tty;
 
        gs_dprintk (GS_DEBUG_BTR, "Entering gs_block_till_ready.\n"); 
        /*
         * If the device is in the middle of being closed, then block
         * until it's done, and then try again.
         */
-       if (tty_hung_up_p(filp) || port->port.flags & ASYNC_CLOSING) {
-               interruptible_sleep_on(&port->port.close_wait);
-               if (port->port.flags & ASYNC_HUP_NOTIFY)
+       if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
+               interruptible_sleep_on(&port->close_wait);
+               if (port->flags & ASYNC_HUP_NOTIFY)
                        return -EAGAIN;
                else
                        return -ERESTARTSYS;
@@ -432,7 +436,7 @@ int gs_block_til_ready(void *port_, struct file * filp)
         */
        if ((filp->f_flags & O_NONBLOCK) ||
            (tty->flags & (1 << TTY_IO_ERROR))) {
-               port->port.flags |= ASYNC_NORMAL_ACTIVE;
+               port->flags |= ASYNC_NORMAL_ACTIVE;
                return 0;
        }
 
@@ -444,34 +448,34 @@ int gs_block_til_ready(void *port_, struct file * filp)
        /*
         * Block waiting for the carrier detect and the line to become
         * free (i.e., not in use by the callout).  While we are in
-        * this loop, port->port.count is dropped by one, so that
+        * this loop, port->count is dropped by one, so that
         * rs_close() knows when to free things.  We restore it upon
         * exit, either normal or abnormal.
         */
        retval = 0;
 
-       add_wait_queue(&port->port.open_wait, &wait);
+       add_wait_queue(&port->open_wait, &wait);
 
        gs_dprintk (GS_DEBUG_BTR, "after add waitq.\n"); 
-       spin_lock_irqsave(&port->driver_lock, flags);
+       spin_lock_irqsave(&port->lock, flags);
        if (!tty_hung_up_p(filp)) {
-               port->port.count--;
+               port->count--;
        }
-       spin_unlock_irqrestore(&port->driver_lock, flags);
-       port->port.blocked_open++;
+       port->blocked_open++;
+       spin_unlock_irqrestore(&port->lock, flags);
        while (1) {
-               CD = port->rd->get_CD (port);
+               CD = tty_port_carrier_raised(port);
                gs_dprintk (GS_DEBUG_BTR, "CD is now %d.\n", CD);
                set_current_state (TASK_INTERRUPTIBLE);
                if (tty_hung_up_p(filp) ||
-                   !(port->port.flags & ASYNC_INITIALIZED)) {
-                       if (port->port.flags & ASYNC_HUP_NOTIFY)
+                   !(port->flags & ASYNC_INITIALIZED)) {
+                       if (port->flags & ASYNC_HUP_NOTIFY)
                                retval = -EAGAIN;
                        else
                                retval = -ERESTARTSYS;
                        break;
                }
-               if (!(port->port.flags & ASYNC_CLOSING) &&
+               if (!(port->flags & ASYNC_CLOSING) &&
                    (do_clocal || CD))
                        break;
                gs_dprintk (GS_DEBUG_BTR, "signal_pending is now: %d (%lx)\n", 
@@ -483,19 +487,20 @@ int gs_block_til_ready(void *port_, struct file * filp)
                schedule();
        }
        gs_dprintk (GS_DEBUG_BTR, "Got out of the loop. (%d)\n",
-                   port->port.blocked_open);
+                   port->blocked_open);
        set_current_state (TASK_RUNNING);
-       remove_wait_queue(&port->port.open_wait, &wait);
+       remove_wait_queue(&port->open_wait, &wait);
+       
+       spin_lock_irqsave(&port->lock, flags);
        if (!tty_hung_up_p(filp)) {
-               port->port.count++;
+               port->count++;
        }
-       port->port.blocked_open--;
-       if (retval)
-               return retval;
-
-       port->port.flags |= ASYNC_NORMAL_ACTIVE;
+       port->blocked_open--;
+       if (retval == 0)
+               port->flags |= ASYNC_NORMAL_ACTIVE;
+       spin_unlock_irqrestore(&port->lock, flags);
        func_exit ();
-       return 0;
+       return retval;
 }                       
 
 
@@ -506,7 +511,7 @@ void gs_close(struct tty_struct * tty, struct file * filp)
        
        func_enter ();
 
-       port = (struct gs_port *) tty->driver_data;
+       port = tty->driver_data;
 
        if (!port) return;
 
@@ -516,10 +521,10 @@ void gs_close(struct tty_struct * tty, struct file * filp)
                port->port.tty = tty;
        }
 
-       spin_lock_irqsave(&port->driver_lock, flags);
+       spin_lock_irqsave(&port->port.lock, flags);
 
        if (tty_hung_up_p(filp)) {
-               spin_unlock_irqrestore(&port->driver_lock, flags);
+               spin_unlock_irqrestore(&port->port.lock, flags);
                if (port->rd->hungup)
                        port->rd->hungup (port);
                func_exit ();
@@ -538,7 +543,7 @@ void gs_close(struct tty_struct * tty, struct file * filp)
 
        if (port->port.count) {
                gs_dprintk(GS_DEBUG_CLOSE, "gs_close port %p: count: %d\n", port, port->port.count);
-               spin_unlock_irqrestore(&port->driver_lock, flags);
+               spin_unlock_irqrestore(&port->port.lock, flags);
                func_exit ();
                return;
        }
@@ -559,8 +564,10 @@ void gs_close(struct tty_struct * tty, struct file * filp)
         * line status register.
         */
 
+       spin_lock_irqsave(&port->driver_lock, flags);
        port->rd->disable_rx_interrupts (port);
        spin_unlock_irqrestore(&port->driver_lock, flags);
+       spin_unlock_irqrestore(&port->port.lock, flags);
 
        /* close has no way of returning "EINTR", so discard return value */
        if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
@@ -573,20 +580,25 @@ void gs_close(struct tty_struct * tty, struct file * filp)
        tty_ldisc_flush(tty);
        tty->closing = 0;
 
+       spin_lock_irqsave(&port->driver_lock, flags);
        port->event = 0;
        port->rd->close (port);
        port->rd->shutdown_port (port);
+       spin_unlock_irqrestore(&port->driver_lock, flags);
+
+       spin_lock_irqsave(&port->port.lock, flags);
        port->port.tty = NULL;
 
        if (port->port.blocked_open) {
                if (port->close_delay) {
-                       spin_unlock_irqrestore(&port->driver_lock, flags);
+                       spin_unlock_irqrestore(&port->port.lock, flags);
                        msleep_interruptible(jiffies_to_msecs(port->close_delay));
-                       spin_lock_irqsave(&port->driver_lock, flags);
+                       spin_lock_irqsave(&port->port.lock, flags);
                }
                wake_up_interruptible(&port->port.open_wait);
        }
        port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING | ASYNC_INITIALIZED);
+       spin_unlock_irqrestore(&port->port.lock, flags);
        wake_up_interruptible(&port->port.close_wait);
 
        func_exit ();