unsigned long overrun_time;
        int num_overrun;
 
+       /* non-atomic */
+       bool no_room;
+
        unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
        unsigned char echo_overrun:1;
 
        return put_user(x, ptr);
 }
 
-/**
- *     n_tty_set_room  -       receive space
- *     @tty: terminal
- *
- *     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 int set_room(struct tty_struct *tty)
+static int receive_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);
 
        if (I_PARMRK(tty)) {
                /* Multiply read_cnt by 3, since each byte might take up to
         */
        if (left <= 0)
                left = ldata->icanon && !ldata->canon_data;
-       old_left = tty->receive_room;
-       tty->receive_room = left;
 
-       raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
-
-       return left && !old_left;
+       return left;
 }
 
+/**
+ *     n_tty_set_room  -       receive space
+ *     @tty: terminal
+ *
+ *     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)
 {
+       struct n_tty_data *ldata = tty->disc_data;
+
        /* Did this open up the receive buffer? We may need to flip */
-       if (set_room(tty)) {
+       if (unlikely(ldata->no_room) && receive_room(tty)) {
+               ldata->no_room = 0;
+
                WARN_RATELIMIT(tty->port->itty == NULL,
                                "scheduling with invalid itty\n");
                /* see if ldisc has been killed - if so, this means that
  *     calls one at a time and in order (or using flush_to_ldisc)
  */
 
-static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
-                             char *fp, int count)
+static void __receive_buf(struct tty_struct *tty, const unsigned char *cp,
+                         char *fp, int count)
 {
        struct n_tty_data *ldata = tty->disc_data;
        const unsigned char *p;
                        tty->ops->flush_chars(tty);
        }
 
-       set_room(tty);
-
        if ((!ldata->icanon && (ldata->read_cnt >= ldata->minimum_to_wake)) ||
                L_EXTPROC(tty)) {
                kill_fasync(&tty->fasync, SIGIO, POLL_IN);
         */
        while (1) {
                tty_set_flow_change(tty, TTY_THROTTLE_SAFE);
-               if (tty->receive_room >= TTY_THRESHOLD_THROTTLE)
+               if (receive_room(tty) >= TTY_THRESHOLD_THROTTLE)
                        break;
                if (!tty_throttle_safe(tty))
                        break;
        __tty_set_flow_change(tty, 0);
 }
 
+static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
+                             char *fp, int count)
+{
+       __receive_buf(tty, cp, fp, count);
+}
+
+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;
+
+       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);
+
+       return count;
+}
+
 int is_ignored(int sig)
 {
        return (sigismember(¤t->blocked, sig) ||
        .receive_buf     = n_tty_receive_buf,
        .write_wakeup    = n_tty_write_wakeup,
        .fasync          = n_tty_fasync,
+       .receive_buf2    = n_tty_receive_buf2,
 };
 
 /**
 
 receive_buf(struct tty_struct *tty, struct tty_buffer *head, int count)
 {
        struct tty_ldisc *disc = tty->ldisc;
+       char          *p = head->char_buf_ptr + head->read;
+       unsigned char *f = head->flag_buf_ptr + head->read;
 
-       count = min_t(int, count, tty->receive_room);
-       if (count)
-               disc->ops->receive_buf(tty, head->char_buf_ptr + head->read,
-                                      head->flag_buf_ptr + head->read, count);
+       if (disc->ops->receive_buf2)
+               count = disc->ops->receive_buf2(tty, p, f, count);
+       else {
+               count = min_t(int, count, tty->receive_room);
+               if (count)
+                       disc->ops->receive_buf(tty, p, f, count);
+       }
        head->read += count;
        return count;
 }
 
 extern void tty_ldisc_deinit(struct tty_struct *tty);
 extern void tty_ldisc_begin(void);
 
+static inline int tty_ldisc_receive_buf(struct tty_ldisc *ld, unsigned char *p,
+                                       char *f, int count)
+{
+       if (ld->ops->receive_buf2)
+               count = ld->ops->receive_buf2(ld->tty, p, f, count);
+       else {
+               count = min_t(int, count, ld->tty->receive_room);
+               if (count)
+                       ld->ops->receive_buf(ld->tty, p, f, count);
+       }
+       return count;
+}
+
 
 /* n_tty.c */
 extern struct tty_ldisc_ops tty_ldisc_N_TTY;
 
  *
  *     Tells the discipline that the DCD pin has changed its status.
  *     Used exclusively by the N_PPS (Pulse-Per-Second) line discipline.
+ *
+ * int (*receive_buf2)(struct tty_struct *, const unsigned char *cp,
+ *                     char *fp, int count);
+ *
+ *     This function is called by the low-level tty driver to send
+ *     characters received by the hardware to the line discpline for
+ *     processing.  <cp> is a pointer to the buffer of input
+ *     character received by the device.  <fp> is a pointer to a
+ *     pointer of flag bytes which indicate whether a character was
+ *     received with a parity error, etc.
+ *     If assigned, prefer this function for automatic flow control.
  */
 
 #include <linux/fs.h>
        void    (*write_wakeup)(struct tty_struct *);
        void    (*dcd_change)(struct tty_struct *, unsigned int);
        void    (*fasync)(struct tty_struct *tty, int on);
+       int     (*receive_buf2)(struct tty_struct *, const unsigned char *cp,
+                               char *fp, int count);
 
        struct  module *owner;