diff options
Diffstat (limited to 'drivers/tty/n_tty.c')
-rw-r--r-- | drivers/tty/n_tty.c | 212 |
1 files changed, 100 insertions, 112 deletions
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 5e9ca4376d68..6af3f3a0b531 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -56,6 +56,8 @@ */ #define WAKEUP_CHARS 256 +#define N_TTY_BUF_SIZE 4096 + /* * This defines the low- and high-watermarks for throttling and * unthrottling the TTY driver. These watermarks are used for @@ -79,14 +81,6 @@ #define ECHO_BLOCK 256 #define ECHO_DISCARD_WATERMARK N_TTY_BUF_SIZE - (ECHO_BLOCK + 32) - -#undef N_TTY_TRACE -#ifdef N_TTY_TRACE -# define n_tty_trace(f, args...) trace_printk(f, ##args) -#else -# define n_tty_trace(f, args...) no_printk(f, ##args) -#endif - struct n_tty_data { /* producer-published */ size_t read_head; @@ -486,18 +480,13 @@ static int do_output_char(u8 c, struct tty_struct *tty, int space) static int process_output(u8 c, struct tty_struct *tty) { struct n_tty_data *ldata = tty->disc_data; - int space, retval; - mutex_lock(&ldata->output_lock); - - space = tty_write_room(tty); - retval = do_output_char(c, tty, space); + guard(mutex)(&ldata->output_lock); - mutex_unlock(&ldata->output_lock); - if (retval < 0) + if (do_output_char(c, tty, tty_write_room(tty)) < 0) return -1; - else - return 0; + + return 0; } /** @@ -522,17 +511,15 @@ static ssize_t process_output_block(struct tty_struct *tty, const u8 *buf, unsigned int nr) { struct n_tty_data *ldata = tty->disc_data; - int space; - int i; + unsigned int space, i; const u8 *cp; - mutex_lock(&ldata->output_lock); + guard(mutex)(&ldata->output_lock); space = tty_write_room(tty); - if (space <= 0) { - mutex_unlock(&ldata->output_lock); - return space; - } + if (space == 0) + return 0; + if (nr > space) nr = space; @@ -544,18 +531,18 @@ static ssize_t process_output_block(struct tty_struct *tty, if (O_ONLRET(tty)) ldata->column = 0; if (O_ONLCR(tty)) - goto break_out; + goto do_write; ldata->canon_column = ldata->column; break; case '\r': if (O_ONOCR(tty) && ldata->column == 0) - goto break_out; + goto do_write; if (O_OCRNL(tty)) - goto break_out; + goto do_write; ldata->canon_column = ldata->column = 0; break; case '\t': - goto break_out; + goto do_write; case '\b': if (ldata->column > 0) ldata->column--; @@ -563,18 +550,15 @@ static ssize_t process_output_block(struct tty_struct *tty, default: if (!iscntrl(c)) { if (O_OLCUC(tty)) - goto break_out; + goto do_write; if (!is_continuation(c, tty)) ldata->column++; } break; } } -break_out: - i = tty->ops->write(tty, buf, i); - - mutex_unlock(&ldata->output_lock); - return i; +do_write: + return tty->ops->write(tty, buf, i); } static int n_tty_process_echo_ops(struct tty_struct *tty, size_t *tail, @@ -696,7 +680,7 @@ static int n_tty_process_echo_ops(struct tty_struct *tty, size_t *tail, static size_t __process_echoes(struct tty_struct *tty) { struct n_tty_data *ldata = tty->disc_data; - int space, old_space; + unsigned int space, old_space; size_t tail; u8 c; @@ -2034,9 +2018,6 @@ static bool canon_copy_from_read_buf(const struct tty_struct *tty, u8 **kbp, tail = MASK(ldata->read_tail); size = min_t(size_t, tail + n, N_TTY_BUF_SIZE); - n_tty_trace("%s: nr:%zu tail:%zu n:%zu size:%zu\n", - __func__, *nr, tail, n, size); - eol = find_next_bit(ldata->read_flags, size, tail); more = n - (size - tail); if (eol == N_TTY_BUF_SIZE && more) { @@ -2054,9 +2035,6 @@ static bool canon_copy_from_read_buf(const struct tty_struct *tty, u8 **kbp, if (!found || read_buf(ldata, eol) != __DISABLED_CHAR) n = c; - n_tty_trace("%s: eol:%zu found:%d n:%zu c:%zu tail:%zu more:%zu\n", - __func__, eol, found, n, c, tail, more); - tty_copy(tty, *kbp, tail, n); *kbp += n; *nr -= n; @@ -2133,6 +2111,66 @@ static int job_control(struct tty_struct *tty, struct file *file) return __tty_check_change(tty, SIGTTIN); } +/* + * We still hold the atomic_read_lock and the termios_rwsem, and can just + * continue to copy data. + */ +static ssize_t n_tty_continue_cookie(struct tty_struct *tty, u8 *kbuf, + size_t nr, void **cookie) +{ + struct n_tty_data *ldata = tty->disc_data; + u8 *kb = kbuf; + + if (ldata->icanon && !L_EXTPROC(tty)) { + /* + * If we have filled the user buffer, see if we should skip an + * EOF character before releasing the lock and returning done. + */ + if (!nr) + canon_skip_eof(ldata); + else if (canon_copy_from_read_buf(tty, &kb, &nr)) + return kb - kbuf; + } else { + if (copy_from_read_buf(tty, &kb, &nr)) + return kb - kbuf; + } + + /* No more data - release locks and stop retries */ + n_tty_kick_worker(tty); + n_tty_check_unthrottle(tty); + up_read(&tty->termios_rwsem); + mutex_unlock(&ldata->atomic_read_lock); + *cookie = NULL; + + return kb - kbuf; +} + +static int n_tty_wait_for_input(struct tty_struct *tty, struct file *file, + struct wait_queue_entry *wait, long *timeout) +{ + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) + return -EIO; + if (tty_hung_up_p(file)) + return 0; + /* + * Abort readers for ttys which never actually get hung up. + * See __tty_hangup(). + */ + if (test_bit(TTY_HUPPING, &tty->flags)) + return 0; + if (!*timeout) + return 0; + if (tty_io_nonblock(tty, file)) + return -EAGAIN; + if (signal_pending(current)) + return -ERESTARTSYS; + + up_read(&tty->termios_rwsem); + *timeout = wait_woken(wait, TASK_INTERRUPTIBLE, *timeout); + down_read(&tty->termios_rwsem); + + return 1; +} /** * n_tty_read - read function for tty @@ -2166,36 +2204,9 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, u8 *kbuf, bool packet; size_t old_tail; - /* - * Is this a continuation of a read started earler? - * - * If so, we still hold the atomic_read_lock and the - * termios_rwsem, and can just continue to copy data. - */ - if (*cookie) { - if (ldata->icanon && !L_EXTPROC(tty)) { - /* - * If we have filled the user buffer, see - * if we should skip an EOF character before - * releasing the lock and returning done. - */ - if (!nr) - canon_skip_eof(ldata); - else if (canon_copy_from_read_buf(tty, &kb, &nr)) - return kb - kbuf; - } else { - if (copy_from_read_buf(tty, &kb, &nr)) - return kb - kbuf; - } - - /* No more data - release locks and stop retries */ - n_tty_kick_worker(tty); - n_tty_check_unthrottle(tty); - up_read(&tty->termios_rwsem); - mutex_unlock(&ldata->atomic_read_lock); - *cookie = NULL; - return kb - kbuf; - } + /* Is this a continuation of a read started earlier? */ + if (*cookie) + return n_tty_continue_cookie(tty, kbuf, nr, cookie); retval = job_control(tty, file); if (retval < 0) @@ -2250,34 +2261,12 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, u8 *kbuf, tty_buffer_flush_work(tty->port); down_read(&tty->termios_rwsem); if (!input_available_p(tty, 0)) { - if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { - retval = -EIO; + int ret = n_tty_wait_for_input(tty, file, &wait, + &timeout); + if (ret <= 0) { + retval = ret; break; } - if (tty_hung_up_p(file)) - break; - /* - * Abort readers for ttys which never actually - * get hung up. See __tty_hangup(). - */ - if (test_bit(TTY_HUPPING, &tty->flags)) - break; - if (!timeout) - break; - if (tty_io_nonblock(tty, file)) { - retval = -EAGAIN; - break; - } - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - up_read(&tty->termios_rwsem); - - timeout = wait_woken(&wait, TASK_INTERRUPTIBLE, - timeout); - - down_read(&tty->termios_rwsem); continue; } } @@ -2292,21 +2281,8 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, u8 *kbuf, nr--; } - /* - * Copy data, and if there is more to be had - * and we have nothing more to wait for, then - * let's mark us for retries. - * - * NOTE! We return here with both the termios_sem - * and atomic_read_lock still held, the retries - * will release them when done. - */ - if (copy_from_read_buf(tty, &kb, &nr) && kb - kbuf >= minimum) { -more_to_be_read: - remove_wait_queue(&tty->read_wait, &wait); - *cookie = cookie; - return kb - kbuf; - } + if (copy_from_read_buf(tty, &kb, &nr) && kb - kbuf >= minimum) + goto more_to_be_read; } n_tty_check_unthrottle(tty); @@ -2333,6 +2309,18 @@ more_to_be_read: retval = kb - kbuf; return retval; +more_to_be_read: + /* + * There is more to be had and we have nothing more to wait for, so + * let's mark us for retries. + * + * NOTE! We return here with both the termios_sem and atomic_read_lock + * still held, the retries will release them when done. + */ + remove_wait_queue(&tty->read_wait, &wait); + *cookie = cookie; + + return kb - kbuf; } /** |