diff options
Diffstat (limited to 'drivers/tty/n_tty.c')
-rw-r--r-- | drivers/tty/n_tty.c | 172 |
1 files changed, 85 insertions, 87 deletions
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 05e72bea9b07..d655416087b7 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -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); } } @@ -189,34 +195,17 @@ static void put_tty_queue(unsigned char c, struct n_tty_data *ldata) } /** - * check_unthrottle - allow new receive data - * @tty; tty device - * - * Check whether to call the driver unthrottle functions - * - * Can sleep, may be called under the atomic_read_lock mutex but - * this is not guaranteed. - */ -static void check_unthrottle(struct tty_struct *tty) -{ - if (tty->count) - tty_unthrottle(tty); -} - -/** * 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); @@ -229,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); } /** @@ -1032,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); } } @@ -1069,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)) { @@ -1236,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); @@ -1251,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; } } @@ -1483,14 +1468,14 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, * mode. We don't want to throttle the driver if we're in * canonical mode and don't have a newline yet! */ - if (tty->receive_room < TTY_THRESHOLD_THROTTLE) - tty_throttle(tty); - - /* FIXME: there is a tiny race here if the receive room check runs - before the other work executes and empties the buffer (upping - the receiving room and unthrottling. We then throttle and get - stuck. This has been observed and traced down by Vincent Pillet/ - We need to address this when we sort out out the rx path locking */ + while (1) { + tty_set_flow_change(tty, TTY_THROTTLE_SAFE); + if (tty->receive_room >= TTY_THRESHOLD_THROTTLE) + break; + if (!tty_throttle_safe(tty)) + break; + } + __tty_set_flow_change(tty, 0); } int is_ignored(int sig) @@ -1607,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); @@ -1645,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: @@ -1740,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) @@ -1753,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; } @@ -1959,10 +1950,17 @@ do_it_again: * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode, * we won't get any more characters. */ - if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) { + while (1) { + tty_set_flow_change(tty, TTY_UNTHROTTLE_SAFE); + if (n_tty_chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE) + break; + if (!tty->count) + break; n_tty_set_room(tty); - check_unthrottle(tty); + if (!tty_unthrottle_safe(tty)) + break; } + __tty_set_flow_change(tty, 0); if (b - buf >= minimum) break; |