From 582f55907d3f6cb762af20b56478bf25ef96430e Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Fri, 17 May 2013 12:49:48 -0400 Subject: tty: Remove TTY_HW_COOK_IN/OUT No in-tree tty driver supports cooked mode in hardware; remove. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) (limited to 'drivers/tty/n_tty.c') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index d655416087b7..905a6fa5250e 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -647,8 +647,7 @@ static void process_echoes(struct tty_struct *tty) if (no_space_left) break; } else { - if (O_OPOST(tty) && - !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) { + if (O_OPOST(tty)) { int retval = do_output_char(c, tty, space); if (retval < 0) break; @@ -1516,12 +1515,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) wake_up_interruptible(&tty->read_wait); ldata->icanon = (L_ICANON(tty) != 0); - if (test_bit(TTY_HW_COOK_IN, &tty->flags)) { - ldata->raw = 1; - ldata->real_raw = 1; - n_tty_set_room(tty); - return; - } + if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) || I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) || I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) || @@ -2037,7 +2031,7 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, retval = -EIO; break; } - if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) { + if (O_OPOST(tty)) { while (nr > 0) { ssize_t num = process_output_block(tty, b, nr); if (num < 0) { -- cgit v1.2.3 From f6c8dbe6e50c6e5121d7b6644718207daa008221 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 07:28:28 -0400 Subject: n_tty: Encapsulate minimum_to_wake within N_TTY minimum_to_wake is unique to N_TTY processing, and belongs in per-ldisc data. Add the ldisc method, ldisc_ops::fasync(), to notify line disciplines when signal-driven I/O is enabled or disabled. When enabled for N_TTY (by fcntl(F_SETFL, O_ASYNC)), blocking reader/polls will be woken for any readable input. When disabled, blocking reader/polls are not woken until the read buffer is full. Canonical mode (L_ICANON(tty), n_tty_data::icanon) is not affected by the minimum_to_wake setting. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) (limited to 'drivers/tty/n_tty.c') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index cdcdb0ea061a..f1806de69b18 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -89,6 +89,7 @@ struct n_tty_data { int read_head; int read_tail; int read_cnt; + int minimum_to_wake; unsigned char *echo_buf; unsigned int echo_pos; @@ -1455,7 +1456,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, n_tty_set_room(tty); - if ((!ldata->icanon && (ldata->read_cnt >= tty->minimum_to_wake)) || + if ((!ldata->icanon && (ldata->read_cnt >= ldata->minimum_to_wake)) || L_EXTPROC(tty)) { kill_fasync(&tty->fasync, SIGIO, POLL_IN); if (waitqueue_active(&tty->read_wait)) @@ -1636,7 +1637,7 @@ static int n_tty_open(struct tty_struct *tty) tty->disc_data = ldata; reset_buffer_flags(tty->disc_data); ldata->column = 0; - tty->minimum_to_wake = 1; + ldata->minimum_to_wake = 1; tty->closing = 0; /* indicate buffer work may resume */ clear_bit(TTY_LDISC_HALTED, &tty->flags); @@ -1804,17 +1805,17 @@ do_it_again: minimum = MIN_CHAR(tty); if (minimum) { if (time) - tty->minimum_to_wake = 1; + ldata->minimum_to_wake = 1; else if (!waitqueue_active(&tty->read_wait) || - (tty->minimum_to_wake > minimum)) - tty->minimum_to_wake = minimum; + (ldata->minimum_to_wake > minimum)) + ldata->minimum_to_wake = minimum; } else { timeout = 0; if (time) { timeout = time; time = 0; } - tty->minimum_to_wake = minimum = 1; + ldata->minimum_to_wake = minimum = 1; } } @@ -1854,9 +1855,9 @@ do_it_again: TASK_RUNNING. */ set_current_state(TASK_INTERRUPTIBLE); - if (((minimum - (b - buf)) < tty->minimum_to_wake) && + if (((minimum - (b - buf)) < ldata->minimum_to_wake) && ((minimum - (b - buf)) >= 1)) - tty->minimum_to_wake = (minimum - (b - buf)); + ldata->minimum_to_wake = (minimum - (b - buf)); if (!input_available_p(tty, 0)) { if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { @@ -1973,7 +1974,7 @@ do_it_again: remove_wait_queue(&tty->read_wait, &wait); if (!waitqueue_active(&tty->read_wait)) - tty->minimum_to_wake = minimum; + ldata->minimum_to_wake = minimum; __set_current_state(TASK_RUNNING); size = b - buf; @@ -2105,6 +2106,7 @@ break_out: static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, poll_table *wait) { + struct n_tty_data *ldata = tty->disc_data; unsigned int mask = 0; poll_wait(file, &tty->read_wait, wait); @@ -2119,9 +2121,9 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, mask |= POLLHUP; if (!(mask & (POLLHUP | POLLIN | POLLRDNORM))) { if (MIN_CHAR(tty) && !TIME_CHAR(tty)) - tty->minimum_to_wake = MIN_CHAR(tty); + ldata->minimum_to_wake = MIN_CHAR(tty); else - tty->minimum_to_wake = 1; + ldata->minimum_to_wake = 1; } if (tty->ops->write && !tty_is_writelocked(tty) && tty_chars_in_buffer(tty) < WAKEUP_CHARS && @@ -2169,6 +2171,18 @@ static int n_tty_ioctl(struct tty_struct *tty, struct file *file, } } +static void n_tty_fasync(struct tty_struct *tty, int on) +{ + struct n_tty_data *ldata = tty->disc_data; + + if (!waitqueue_active(&tty->read_wait)) { + if (on) + ldata->minimum_to_wake = 1; + else if (!tty->fasync) + ldata->minimum_to_wake = N_TTY_BUF_SIZE; + } +} + struct tty_ldisc_ops tty_ldisc_N_TTY = { .magic = TTY_LDISC_MAGIC, .name = "n_tty", @@ -2182,7 +2196,8 @@ struct tty_ldisc_ops tty_ldisc_N_TTY = { .set_termios = n_tty_set_termios, .poll = n_tty_poll, .receive_buf = n_tty_receive_buf, - .write_wakeup = n_tty_write_wakeup + .write_wakeup = n_tty_write_wakeup, + .fasync = n_tty_fasync, }; /** -- cgit v1.2.3 From a6e54319a7499bf754efb3a2cb2f5d4901ccbcff Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 07:28:29 -0400 Subject: n_tty: Untangle read completion variables Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/tty/n_tty.c') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index f1806de69b18..fa5cb4654c4f 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1801,20 +1801,16 @@ do_it_again: minimum = time = 0; timeout = MAX_SCHEDULE_TIMEOUT; if (!ldata->icanon) { - time = (HZ / 10) * TIME_CHAR(tty); minimum = MIN_CHAR(tty); if (minimum) { + time = (HZ / 10) * TIME_CHAR(tty); if (time) ldata->minimum_to_wake = 1; else if (!waitqueue_active(&tty->read_wait) || (ldata->minimum_to_wake > minimum)) ldata->minimum_to_wake = minimum; } else { - timeout = 0; - if (time) { - timeout = time; - time = 0; - } + timeout = (HZ / 10) * TIME_CHAR(tty); ldata->minimum_to_wake = minimum = 1; } } -- cgit v1.2.3 From b84830527645dfe7b7a5cc03518e3c791b4ee9e0 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 07:28:30 -0400 Subject: n_tty: Fix unsafe update of available buffer space receive_room is used to control the amount of data the flip buffer work can push to the read buffer. This update is unsafe: CPU 0 | CPU 1 | | n_tty_read() | n_tty_set_room() | left = n_tty_receive_buf() | | n_tty_set_room() | left = | tty->receive_room = left | | tty->receive_room = left receive_room is now updated with a stale calculation of the available buffer space, and the subsequent work loop will likely overwrite unread data in the input buffer. Update receive_room atomically with the calculation of the available buffer space. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'drivers/tty/n_tty.c') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index fa5cb4654c4f..064616542cb5 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -115,13 +115,14 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x, } /** - * n_tty_set__room - receive space + * n_tty_set_room - receive space * @tty: terminal * - * Called by the driver to find out how much data it is - * permitted to feed to the line discipline without any being lost - * and thus to manage flow control. Not serialized. Answers for the - * "instant". + * Sets tty->receive_room to reflect the currently available space + * in the input buffer, and re-schedules the flip buffer work if space + * just became available. + * + * Locks: Concurrent update is protected with read_lock */ static void n_tty_set_room(struct tty_struct *tty) @@ -129,8 +130,10 @@ static void n_tty_set_room(struct tty_struct *tty) struct n_tty_data *ldata = tty->disc_data; int left; int old_left; + unsigned long flags; + + raw_spin_lock_irqsave(&ldata->read_lock, flags); - /* ldata->read_cnt is not read locked ? */ if (I_PARMRK(tty)) { /* Multiply read_cnt by 3, since each byte might take up to * three times as many spaces when PARMRK is set (depending on @@ -150,6 +153,8 @@ static void n_tty_set_room(struct tty_struct *tty) old_left = tty->receive_room; tty->receive_room = left; + raw_spin_unlock_irqrestore(&ldata->read_lock, flags); + /* Did this open up the receive buffer? We may need to flip */ if (left && !old_left) { WARN_RATELIMIT(tty->port->itty == NULL, @@ -1872,7 +1877,6 @@ do_it_again: retval = -ERESTARTSYS; break; } - /* FIXME: does n_tty_set_room need locking ? */ n_tty_set_room(tty); timeout = schedule_timeout(timeout); continue; -- cgit v1.2.3 From 7879a9f9fd4ff4a88b3c51f4ab6718dc7f86ed86 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 07:28:31 -0400 Subject: n_tty: Buffer work should not reschedule itself Although the driver-side input path must update the available buffer space, it should not reschedule itself. If space is still available and the flip buffers are not empty, flush_to_ldisc() will loop again. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers/tty/n_tty.c') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 064616542cb5..4bf0fc0843d7 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -118,14 +118,14 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x, * n_tty_set_room - receive space * @tty: terminal * - * Sets tty->receive_room to reflect the currently available space + * Updates tty->receive_room to reflect the currently available space * in the input buffer, and re-schedules the flip buffer work if space * just became available. * * Locks: Concurrent update is protected with read_lock */ -static void n_tty_set_room(struct tty_struct *tty) +static int set_room(struct tty_struct *tty) { struct n_tty_data *ldata = tty->disc_data; int left; @@ -155,8 +155,13 @@ static void n_tty_set_room(struct tty_struct *tty) raw_spin_unlock_irqrestore(&ldata->read_lock, flags); + return left && !old_left; +} + +static void n_tty_set_room(struct tty_struct *tty) +{ /* Did this open up the receive buffer? We may need to flip */ - if (left && !old_left) { + if (set_room(tty)) { WARN_RATELIMIT(tty->port->itty == NULL, "scheduling with invalid itty\n"); /* see if ldisc has been killed - if so, this means that @@ -1459,7 +1464,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, tty->ops->flush_chars(tty); } - n_tty_set_room(tty); + set_room(tty); if ((!ldata->icanon && (ldata->read_cnt >= ldata->minimum_to_wake)) || L_EXTPROC(tty)) { -- cgit v1.2.3