[SCSI] pm80xx: WWN Modification for PM8081/88/89 controllers
[cascardo/linux.git] / drivers / tty / n_tty.c
index e3a9321..d655416 100644 (file)
@@ -153,6 +153,12 @@ static void n_tty_set_room(struct tty_struct *tty)
        if (left && !old_left) {
                WARN_RATELIMIT(tty->port->itty == NULL,
                                "scheduling with invalid itty\n");
+               /* see if ldisc has been killed - if so, this means that
+                * even though the ldisc has been halted and ->buf.work
+                * cancelled, ->buf.work is about to be rescheduled
+                */
+               WARN_RATELIMIT(test_bit(TTY_LDISC_HALTED, &tty->flags),
+                              "scheduling buffer work for halted ldisc\n");
                schedule_work(&tty->port->buf.work);
        }
 }
@@ -192,16 +198,14 @@ static void put_tty_queue(unsigned char c, struct n_tty_data *ldata)
  *     reset_buffer_flags      -       reset buffer state
  *     @tty: terminal to reset
  *
- *     Reset the read buffer counters, clear the flags,
- *     and make sure the driver is unthrottled. Called
- *     from n_tty_open() and n_tty_flush_buffer().
+ *     Reset the read buffer counters and clear the flags.
+ *     Called from n_tty_open() and n_tty_flush_buffer().
  *
  *     Locking: tty_read_lock for read fields.
  */
 
-static void reset_buffer_flags(struct tty_struct *tty)
+static void reset_buffer_flags(struct n_tty_data *ldata)
 {
-       struct n_tty_data *ldata = tty->disc_data;
        unsigned long flags;
 
        raw_spin_lock_irqsave(&ldata->read_lock, flags);
@@ -214,36 +218,38 @@ static void reset_buffer_flags(struct tty_struct *tty)
 
        ldata->canon_head = ldata->canon_data = ldata->erasing = 0;
        bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
-       n_tty_set_room(tty);
+}
+
+static void n_tty_packet_mode_flush(struct tty_struct *tty)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&tty->ctrl_lock, flags);
+       if (tty->link->packet) {
+               tty->ctrl_status |= TIOCPKT_FLUSHREAD;
+               wake_up_interruptible(&tty->link->read_wait);
+       }
+       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
 }
 
 /**
  *     n_tty_flush_buffer      -       clean input queue
  *     @tty:   terminal device
  *
- *     Flush the input buffer. Called when the line discipline is
- *     being closed, when the tty layer wants the buffer flushed (eg
- *     at hangup) or when the N_TTY line discipline internally has to
- *     clean the pending queue (for example some signals).
+ *     Flush the input buffer. Called when the tty layer wants the
+ *     buffer flushed (eg at hangup) or when the N_TTY line discipline
+ *     internally has to clean the pending queue (for example some signals).
  *
  *     Locking: ctrl_lock, read_lock.
  */
 
 static void n_tty_flush_buffer(struct tty_struct *tty)
 {
-       unsigned long flags;
-       /* clear everything and unthrottle the driver */
-       reset_buffer_flags(tty);
-
-       if (!tty->link)
-               return;
+       reset_buffer_flags(tty->disc_data);
+       n_tty_set_room(tty);
 
-       spin_lock_irqsave(&tty->ctrl_lock, flags);
-       if (tty->link->packet) {
-               tty->ctrl_status |= TIOCPKT_FLUSHREAD;
-               wake_up_interruptible(&tty->link->read_wait);
-       }
-       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+       if (tty->link)
+               n_tty_packet_mode_flush(tty);
 }
 
 /**
@@ -1017,23 +1023,19 @@ static void eraser(unsigned char c, struct tty_struct *tty)
  *     isig            -       handle the ISIG optio
  *     @sig: signal
  *     @tty: terminal
- *     @flush: force flush
  *
- *     Called when a signal is being sent due to terminal input. This
- *     may caus terminal flushing to take place according to the termios
- *     settings and character used. Called from the driver receive_buf
- *     path so serialized.
+ *     Called when a signal is being sent due to terminal input.
+ *     Called from the driver receive_buf path so serialized.
  *
- *     Locking: ctrl_lock, read_lock (both via flush buffer)
+ *     Locking: ctrl_lock
  */
 
-static inline void isig(int sig, struct tty_struct *tty, int flush)
+static inline void isig(int sig, struct tty_struct *tty)
 {
-       if (tty->pgrp)
-               kill_pgrp(tty->pgrp, sig, 1);
-       if (flush || !L_NOFLSH(tty)) {
-               n_tty_flush_buffer(tty);
-               tty_driver_flush_buffer(tty);
+       struct pid *tty_pgrp = tty_get_pgrp(tty);
+       if (tty_pgrp) {
+               kill_pgrp(tty_pgrp, sig, 1);
+               put_pid(tty_pgrp);
        }
 }
 
@@ -1054,7 +1056,11 @@ static inline void n_tty_receive_break(struct tty_struct *tty)
        if (I_IGNBRK(tty))
                return;
        if (I_BRKINT(tty)) {
-               isig(SIGINT, tty, 1);
+               isig(SIGINT, tty);
+               if (!L_NOFLSH(tty)) {
+                       n_tty_flush_buffer(tty);
+                       tty_driver_flush_buffer(tty);
+               }
                return;
        }
        if (I_PARMRK(tty)) {
@@ -1221,11 +1227,6 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
                signal = SIGTSTP;
                if (c == SUSP_CHAR(tty)) {
 send_signal:
-                       /*
-                        * Note that we do not use isig() here because we want
-                        * the order to be:
-                        * 1) flush, 2) echo, 3) signal
-                        */
                        if (!L_NOFLSH(tty)) {
                                n_tty_flush_buffer(tty);
                                tty_driver_flush_buffer(tty);
@@ -1236,8 +1237,7 @@ send_signal:
                                echo_char(c, tty);
                                process_echoes(tty);
                        }
-                       if (tty->pgrp)
-                               kill_pgrp(tty->pgrp, signal, 1);
+                       isig(signal, tty);
                        return;
                }
        }
@@ -1592,7 +1592,9 @@ static void n_tty_close(struct tty_struct *tty)
 {
        struct n_tty_data *ldata = tty->disc_data;
 
-       n_tty_flush_buffer(tty);
+       if (tty->link)
+               n_tty_packet_mode_flush(tty);
+
        kfree(ldata->read_buf);
        kfree(ldata->echo_buf);
        kfree(ldata);
@@ -1630,12 +1632,14 @@ static int n_tty_open(struct tty_struct *tty)
                goto err_free_bufs;
 
        tty->disc_data = ldata;
-       reset_buffer_flags(tty);
-       tty_unthrottle(tty);
+       reset_buffer_flags(tty->disc_data);
        ldata->column = 0;
-       n_tty_set_termios(tty, NULL);
        tty->minimum_to_wake = 1;
        tty->closing = 0;
+       /* indicate buffer work may resume */
+       clear_bit(TTY_LDISC_HALTED, &tty->flags);
+       n_tty_set_termios(tty, NULL);
+       tty_unthrottle(tty);
 
        return 0;
 err_free_bufs:
@@ -1725,10 +1729,9 @@ extern ssize_t redirected_tty_write(struct file *, const char __user *,
  *     and if appropriate send any needed signals and return a negative
  *     error code if action should be taken.
  *
- *     FIXME:
- *     Locking: None - redirected write test is safe, testing
- *     current->signal should possibly lock current->sighand
- *     pgrp locking ?
+ *     Locking: redirected write test is safe
+ *              current->signal->tty check is safe
+ *              ctrl_lock to safely reference tty->pgrp
  */
 
 static int job_control(struct tty_struct *tty, struct file *file)
@@ -1738,19 +1741,22 @@ static int job_control(struct tty_struct *tty, struct file *file)
        /* NOTE: not yet done after every sleep pending a thorough
           check of the logic of this change. -- jlc */
        /* don't stop on /dev/console */
-       if (file->f_op->write != redirected_tty_write &&
-           current->signal->tty == tty) {
-               if (!tty->pgrp)
-                       printk(KERN_ERR "n_tty_read: no tty->pgrp!\n");
-               else if (task_pgrp(current) != tty->pgrp) {
-                       if (is_ignored(SIGTTIN) ||
-                           is_current_pgrp_orphaned())
-                               return -EIO;
-                       kill_pgrp(task_pgrp(current), SIGTTIN, 1);
-                       set_thread_flag(TIF_SIGPENDING);
-                       return -ERESTARTSYS;
-               }
+       if (file->f_op->write == redirected_tty_write ||
+           current->signal->tty != tty)
+               return 0;
+
+       spin_lock_irq(&tty->ctrl_lock);
+       if (!tty->pgrp)
+               printk(KERN_ERR "n_tty_read: no tty->pgrp!\n");
+       else if (task_pgrp(current) != tty->pgrp) {
+               spin_unlock_irq(&tty->ctrl_lock);
+               if (is_ignored(SIGTTIN) || is_current_pgrp_orphaned())
+                       return -EIO;
+               kill_pgrp(task_pgrp(current), SIGTTIN, 1);
+               set_thread_flag(TIF_SIGPENDING);
+               return -ERESTARTSYS;
        }
+       spin_unlock_irq(&tty->ctrl_lock);
        return 0;
 }