Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/trivial
[firefly-linux-kernel-4.4.55.git] / drivers / tty / n_tty.c
index 6aa90332dd3e14e8722dec8329bcf90d1b09437b..c9a9ddd1d0bc2aa5091d7e1678920df2d9292429 100644 (file)
@@ -50,6 +50,7 @@
 #include <linux/uaccess.h>
 #include <linux/module.h>
 #include <linux/ratelimit.h>
+#include <linux/vmalloc.h>
 
 
 /* number of characters left in xmit buffer before select has we have room */
@@ -263,7 +264,7 @@ static void n_tty_check_throttle(struct tty_struct *tty)
        __tty_set_flow_change(tty, 0);
 }
 
-static inline void n_tty_check_unthrottle(struct tty_struct *tty)
+static void n_tty_check_unthrottle(struct tty_struct *tty)
 {
        if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
            tty->link->ldisc->ops->write_wakeup == n_tty_write_wakeup) {
@@ -315,12 +316,9 @@ static inline void n_tty_check_unthrottle(struct tty_struct *tty)
  *     not active.
  */
 
-static void put_tty_queue(unsigned char c, struct n_tty_data *ldata)
+static inline void put_tty_queue(unsigned char c, struct n_tty_data *ldata)
 {
-       if (read_cnt(ldata) < N_TTY_BUF_SIZE) {
-               *read_buf_addr(ldata, ldata->read_head) = c;
-               ldata->read_head++;
-       }
+       *read_buf_addr(ldata, ldata->read_head++) = c;
 }
 
 /**
@@ -1112,7 +1110,7 @@ static void eraser(unsigned char c, struct tty_struct *tty)
  *     Locking: ctrl_lock
  */
 
-static inline void isig(int sig, struct tty_struct *tty)
+static void isig(int sig, struct tty_struct *tty)
 {
        struct pid *tty_pgrp = tty_get_pgrp(tty);
        if (tty_pgrp) {
@@ -1135,7 +1133,7 @@ static inline void isig(int sig, struct tty_struct *tty)
  *     Note: may get exclusive termios_rwsem if flushing input buffer
  */
 
-static inline void n_tty_receive_break(struct tty_struct *tty)
+static void n_tty_receive_break(struct tty_struct *tty)
 {
        struct n_tty_data *ldata = tty->disc_data;
 
@@ -1173,7 +1171,7 @@ static inline void n_tty_receive_break(struct tty_struct *tty)
  *     private.
  */
 
-static inline void n_tty_receive_overrun(struct tty_struct *tty)
+static void n_tty_receive_overrun(struct tty_struct *tty)
 {
        struct n_tty_data *ldata = tty->disc_data;
        char buf[64];
@@ -1201,8 +1199,7 @@ static inline void n_tty_receive_overrun(struct tty_struct *tty)
  *             caller holds non-exclusive termios_rwsem
  *             publishes read_head via put_tty_queue()
  */
-static inline void n_tty_receive_parity_error(struct tty_struct *tty,
-                                             unsigned char c)
+static void n_tty_receive_parity_error(struct tty_struct *tty, unsigned char c)
 {
        struct n_tty_data *ldata = tty->disc_data;
 
@@ -1252,103 +1249,49 @@ n_tty_receive_signal_char(struct tty_struct *tty, int signal, unsigned char c)
  *             caller holds non-exclusive termios_rwsem
  *             publishes canon_head if canonical mode is active
  *             otherwise, publishes read_head via put_tty_queue()
+ *
+ *     Returns 1 if LNEXT was received, else returns 0
  */
 
-static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
+static int
+n_tty_receive_char_special(struct tty_struct *tty, unsigned char c)
 {
        struct n_tty_data *ldata = tty->disc_data;
        int parmrk;
 
-       if (ldata->raw) {
-               put_tty_queue(c, ldata);
-               return;
-       }
-
-       if (I_ISTRIP(tty))
-               c &= 0x7f;
-       if (I_IUCLC(tty) && L_IEXTEN(tty))
-               c = tolower(c);
-
-       if (L_EXTPROC(tty)) {
-               put_tty_queue(c, ldata);
-               return;
-       }
-
-       if (tty->stopped && !tty->flow_stopped && I_IXON(tty) &&
-           I_IXANY(tty) && c != START_CHAR(tty) && c != STOP_CHAR(tty) &&
-           c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) && c != SUSP_CHAR(tty)) {
-               start_tty(tty);
-               process_echoes(tty);
-       }
-
-       if (tty->closing) {
-               if (I_IXON(tty)) {
-                       if (c == START_CHAR(tty)) {
-                               start_tty(tty);
-                               process_echoes(tty);
-                       } else if (c == STOP_CHAR(tty))
-                               stop_tty(tty);
-               }
-               return;
-       }
-
-       /*
-        * If the previous character was LNEXT, or we know that this
-        * character is not one of the characters that we'll have to
-        * handle specially, do shortcut processing to speed things
-        * up.
-        */
-       if (!test_bit(c, ldata->char_map) || ldata->lnext) {
-               ldata->lnext = 0;
-               parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0;
-               if (read_cnt(ldata) >= (N_TTY_BUF_SIZE - parmrk - 1)) {
-                       /* beep if no space */
-                       if (L_ECHO(tty))
-                               process_output('\a', tty);
-                       return;
-               }
-               if (L_ECHO(tty)) {
-                       finish_erasing(ldata);
-                       /* Record the column of first canon char. */
-                       if (ldata->canon_head == ldata->read_head)
-                               echo_set_canon_col(ldata);
-                       echo_char(c, tty);
-                       commit_echoes(tty);
-               }
-               if (parmrk)
-                       put_tty_queue(c, ldata);
-               put_tty_queue(c, ldata);
-               return;
-       }
-
        if (I_IXON(tty)) {
                if (c == START_CHAR(tty)) {
                        start_tty(tty);
                        commit_echoes(tty);
-                       return;
+                       return 0;
                }
                if (c == STOP_CHAR(tty)) {
                        stop_tty(tty);
-                       return;
+                       return 0;
                }
        }
 
        if (L_ISIG(tty)) {
                if (c == INTR_CHAR(tty)) {
                        n_tty_receive_signal_char(tty, SIGINT, c);
-                       return;
+                       return 0;
                } else if (c == QUIT_CHAR(tty)) {
                        n_tty_receive_signal_char(tty, SIGQUIT, c);
-                       return;
+                       return 0;
                } else if (c == SUSP_CHAR(tty)) {
                        n_tty_receive_signal_char(tty, SIGTSTP, c);
-                       return;
+                       return 0;
                }
        }
 
+       if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty)) {
+               start_tty(tty);
+               process_echoes(tty);
+       }
+
        if (c == '\r') {
                if (I_IGNCR(tty))
-                       return;
+                       return 0;
                if (I_ICRNL(tty))
                        c = '\n';
        } else if (c == '\n' && I_INLCR(tty))
@@ -1359,7 +1302,7 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
                    (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) {
                        eraser(c, tty);
                        commit_echoes(tty);
-                       return;
+                       return 0;
                }
                if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) {
                        ldata->lnext = 1;
@@ -1371,10 +1314,9 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
                                        commit_echoes(tty);
                                }
                        }
-                       return;
+                       return 1;
                }
-               if (c == REPRINT_CHAR(tty) && L_ECHO(tty) &&
-                   L_IEXTEN(tty)) {
+               if (c == REPRINT_CHAR(tty) && L_ECHO(tty) && L_IEXTEN(tty)) {
                        size_t tail = ldata->canon_head;
 
                        finish_erasing(ldata);
@@ -1385,14 +1327,9 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
                                tail++;
                        }
                        commit_echoes(tty);
-                       return;
+                       return 0;
                }
                if (c == '\n') {
-                       if (read_cnt(ldata) >= N_TTY_BUF_SIZE) {
-                               if (L_ECHO(tty))
-                                       process_output('\a', tty);
-                               return;
-                       }
                        if (L_ECHO(tty) || L_ECHONL(tty)) {
                                echo_char_raw('\n', ldata);
                                commit_echoes(tty);
@@ -1400,8 +1337,6 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
                        goto handle_newline;
                }
                if (c == EOF_CHAR(tty)) {
-                       if (read_cnt(ldata) >= N_TTY_BUF_SIZE)
-                               return;
                        c = __DISABLED_CHAR;
                        goto handle_newline;
                }
@@ -1409,11 +1344,6 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
                    (c == EOL2_CHAR(tty) && L_IEXTEN(tty))) {
                        parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty))
                                 ? 1 : 0;
-                       if (read_cnt(ldata) >= (N_TTY_BUF_SIZE - parmrk)) {
-                               if (L_ECHO(tty))
-                                       process_output('\a', tty);
-                               return;
-                       }
                        /*
                         * XXX are EOL_CHAR and EOL2_CHAR echoed?!?
                         */
@@ -1438,17 +1368,11 @@ handle_newline:
                        kill_fasync(&tty->fasync, SIGIO, POLL_IN);
                        if (waitqueue_active(&tty->read_wait))
                                wake_up_interruptible(&tty->read_wait);
-                       return;
+                       return 0;
                }
        }
 
        parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0;
-       if (read_cnt(ldata) >= (N_TTY_BUF_SIZE - parmrk - 1)) {
-               /* beep if no space */
-               if (L_ECHO(tty))
-                       process_output('\a', tty);
-               return;
-       }
        if (L_ECHO(tty)) {
                finish_erasing(ldata);
                if (c == '\n')
@@ -1466,6 +1390,77 @@ handle_newline:
                put_tty_queue(c, ldata);
 
        put_tty_queue(c, ldata);
+       return 0;
+}
+
+static inline void
+n_tty_receive_char_inline(struct tty_struct *tty, unsigned char c)
+{
+       struct n_tty_data *ldata = tty->disc_data;
+       int parmrk;
+
+       if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty)) {
+               start_tty(tty);
+               process_echoes(tty);
+       }
+       if (L_ECHO(tty)) {
+               finish_erasing(ldata);
+               /* Record the column of first canon char. */
+               if (ldata->canon_head == ldata->read_head)
+                       echo_set_canon_col(ldata);
+               echo_char(c, tty);
+               commit_echoes(tty);
+       }
+       parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0;
+       if (parmrk)
+               put_tty_queue(c, ldata);
+       put_tty_queue(c, ldata);
+}
+
+static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
+{
+       n_tty_receive_char_inline(tty, c);
+}
+
+static inline void
+n_tty_receive_char_fast(struct tty_struct *tty, unsigned char c)
+{
+       struct n_tty_data *ldata = tty->disc_data;
+
+       if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty)) {
+               start_tty(tty);
+               process_echoes(tty);
+       }
+       if (L_ECHO(tty)) {
+               finish_erasing(ldata);
+               /* Record the column of first canon char. */
+               if (ldata->canon_head == ldata->read_head)
+                       echo_set_canon_col(ldata);
+               echo_char(c, tty);
+               commit_echoes(tty);
+       }
+       put_tty_queue(c, ldata);
+}
+
+static inline void
+n_tty_receive_char_closing(struct tty_struct *tty, unsigned char c)
+{
+       if (I_ISTRIP(tty))
+               c &= 0x7f;
+       if (I_IUCLC(tty) && L_IEXTEN(tty))
+               c = tolower(c);
+
+       if (I_IXON(tty)) {
+               if (c == STOP_CHAR(tty))
+                       stop_tty(tty);
+               else if (c == START_CHAR(tty) ||
+                        (tty->stopped && !tty->flow_stopped && I_IXANY(tty) &&
+                         c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) &&
+                         c != SUSP_CHAR(tty))) {
+                       start_tty(tty);
+                       process_echoes(tty);
+               }
+       }
 }
 
 static void
@@ -1491,6 +1486,22 @@ n_tty_receive_char_flagged(struct tty_struct *tty, unsigned char c, char flag)
        }
 }
 
+static void
+n_tty_receive_char_lnext(struct tty_struct *tty, unsigned char c, char flag)
+{
+       struct n_tty_data *ldata = tty->disc_data;
+
+       ldata->lnext = 0;
+       if (likely(flag == TTY_NORMAL)) {
+               if (I_ISTRIP(tty))
+                       c &= 0x7f;
+               if (I_IUCLC(tty) && L_IEXTEN(tty))
+                       c = tolower(c);
+               n_tty_receive_char(tty, c);
+       } else
+               n_tty_receive_char_flagged(tty, c, flag);
+}
+
 /**
  *     n_tty_receive_buf       -       data receive
  *     @tty: terminal device
@@ -1530,25 +1541,126 @@ n_tty_receive_buf_real_raw(struct tty_struct *tty, const unsigned char *cp,
        ldata->read_head += n;
 }
 
+static void
+n_tty_receive_buf_raw(struct tty_struct *tty, const unsigned char *cp,
+                     char *fp, int count)
+{
+       struct n_tty_data *ldata = tty->disc_data;
+       char flag = TTY_NORMAL;
+
+       while (count--) {
+               if (fp)
+                       flag = *fp++;
+               if (likely(flag == TTY_NORMAL))
+                       put_tty_queue(*cp++, ldata);
+               else
+                       n_tty_receive_char_flagged(tty, *cp++, flag);
+       }
+}
+
+static void
+n_tty_receive_buf_closing(struct tty_struct *tty, const unsigned char *cp,
+                         char *fp, int count)
+{
+       char flag = TTY_NORMAL;
+
+       while (count--) {
+               if (fp)
+                       flag = *fp++;
+               if (likely(flag == TTY_NORMAL))
+                       n_tty_receive_char_closing(tty, *cp++);
+               else
+                       n_tty_receive_char_flagged(tty, *cp++, flag);
+       }
+}
+
+static void
+n_tty_receive_buf_standard(struct tty_struct *tty, const unsigned char *cp,
+                         char *fp, int count)
+{
+       struct n_tty_data *ldata = tty->disc_data;
+       char flag = TTY_NORMAL;
+
+       while (count--) {
+               if (fp)
+                       flag = *fp++;
+               if (likely(flag == TTY_NORMAL)) {
+                       unsigned char c = *cp++;
+
+                       if (I_ISTRIP(tty))
+                               c &= 0x7f;
+                       if (I_IUCLC(tty) && L_IEXTEN(tty))
+                               c = tolower(c);
+                       if (L_EXTPROC(tty)) {
+                               put_tty_queue(c, ldata);
+                               continue;
+                       }
+                       if (!test_bit(c, ldata->char_map))
+                               n_tty_receive_char_inline(tty, c);
+                       else if (n_tty_receive_char_special(tty, c) && count) {
+                               if (fp)
+                                       flag = *fp++;
+                               n_tty_receive_char_lnext(tty, *cp++, flag);
+                               count--;
+                       }
+               } else
+                       n_tty_receive_char_flagged(tty, *cp++, flag);
+       }
+}
+
+static void
+n_tty_receive_buf_fast(struct tty_struct *tty, const unsigned char *cp,
+                      char *fp, int count)
+{
+       struct n_tty_data *ldata = tty->disc_data;
+       char flag = TTY_NORMAL;
+
+       while (count--) {
+               if (fp)
+                       flag = *fp++;
+               if (likely(flag == TTY_NORMAL)) {
+                       unsigned char c = *cp++;
+
+                       if (!test_bit(c, ldata->char_map))
+                               n_tty_receive_char_fast(tty, c);
+                       else if (n_tty_receive_char_special(tty, c) && count) {
+                               if (fp)
+                                       flag = *fp++;
+                               n_tty_receive_char_lnext(tty, *cp++, flag);
+                               count--;
+                       }
+               } else
+                       n_tty_receive_char_flagged(tty, *cp++, flag);
+       }
+}
+
 static void __receive_buf(struct tty_struct *tty, const unsigned char *cp,
                          char *fp, int count)
 {
        struct n_tty_data *ldata = tty->disc_data;
+       bool preops = I_ISTRIP(tty) || (I_IUCLC(tty) && L_IEXTEN(tty));
 
        if (ldata->real_raw)
                n_tty_receive_buf_real_raw(tty, cp, fp, count);
+       else if (ldata->raw || (L_EXTPROC(tty) && !preops))
+               n_tty_receive_buf_raw(tty, cp, fp, count);
+       else if (tty->closing && !L_EXTPROC(tty))
+               n_tty_receive_buf_closing(tty, cp, fp, count);
        else {
-               char flag = TTY_NORMAL;
+               if (ldata->lnext) {
+                       char flag = TTY_NORMAL;
 
-               while (count--) {
                        if (fp)
                                flag = *fp++;
-                       if (likely(flag == TTY_NORMAL))
-                               n_tty_receive_char(tty, *cp++);
-                       else
-                               n_tty_receive_char_flagged(tty, *cp++, flag);
+                       n_tty_receive_char_lnext(tty, *cp++, flag);
+                       count--;
                }
 
+               if (!preops && !I_PARMRK(tty))
+                       n_tty_receive_buf_fast(tty, cp, fp, count);
+               else
+                       n_tty_receive_buf_standard(tty, cp, fp, count);
+
                flush_echoes(tty);
                if (tty->ops->flush_chars)
                        tty->ops->flush_chars(tty);
@@ -1565,8 +1677,23 @@ static void __receive_buf(struct tty_struct *tty, const unsigned char *cp,
 static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
                              char *fp, int count)
 {
+       int room, n;
+
        down_read(&tty->termios_rwsem);
-       __receive_buf(tty, cp, fp, count);
+
+       while (1) {
+               room = receive_room(tty);
+               n = min(count, room);
+               if (!n)
+                       break;
+               __receive_buf(tty, cp, fp, n);
+               cp += n;
+               if (fp)
+                       fp += n;
+               count -= n;
+       }
+
+       tty->receive_room = room;
        n_tty_check_throttle(tty);
        up_read(&tty->termios_rwsem);
 }
@@ -1575,22 +1702,31 @@ static int n_tty_receive_buf2(struct tty_struct *tty, const unsigned char *cp,
                              char *fp, int count)
 {
        struct n_tty_data *ldata = tty->disc_data;
-       int room;
+       int room, n, rcvd = 0;
 
        down_read(&tty->termios_rwsem);
 
-       tty->receive_room = room = receive_room(tty);
-       if (!room)
-               ldata->no_room = 1;
-       count = min(count, room);
-       if (count) {
-               __receive_buf(tty, cp, fp, count);
-               n_tty_check_throttle(tty);
+       while (1) {
+               room = receive_room(tty);
+               n = min(count, room);
+               if (!n) {
+                       if (!room)
+                               ldata->no_room = 1;
+                       break;
+               }
+               __receive_buf(tty, cp, fp, n);
+               cp += n;
+               if (fp)
+                       fp += n;
+               count -= n;
+               rcvd += n;
        }
 
+       tty->receive_room = room;
+       n_tty_check_throttle(tty);
        up_read(&tty->termios_rwsem);
 
-       return count;
+       return rcvd;
 }
 
 int is_ignored(int sig)
@@ -1986,6 +2122,17 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
        if (c < 0)
                return c;
 
+       /*
+        *      Internal serialization of reads.
+        */
+       if (file->f_flags & O_NONBLOCK) {
+               if (!mutex_trylock(&ldata->atomic_read_lock))
+                       return -EAGAIN;
+       } else {
+               if (mutex_lock_interruptible(&ldata->atomic_read_lock))
+                       return -ERESTARTSYS;
+       }
+
        down_read(&tty->termios_rwsem);
 
        minimum = time = 0;
@@ -2005,20 +2152,6 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
                }
        }
 
-       /*
-        *      Internal serialization of reads.
-        */
-       if (file->f_flags & O_NONBLOCK) {
-               if (!mutex_trylock(&ldata->atomic_read_lock)) {
-                       up_read(&tty->termios_rwsem);
-                       return -EAGAIN;
-               }
-       } else {
-               if (mutex_lock_interruptible(&ldata->atomic_read_lock)) {
-                       up_read(&tty->termios_rwsem);
-                       return -ERESTARTSYS;
-               }
-       }
        packet = tty->packet;
 
        add_wait_queue(&tty->read_wait, &wait);