/* consumer-published */
        size_t read_tail;
+       size_t line_start;
 
        /* protected by output lock */
        unsigned int column;
 {
        ldata->read_head = ldata->canon_head = ldata->read_tail = 0;
        ldata->echo_head = ldata->echo_tail = ldata->echo_commit = 0;
+       ldata->line_start = 0;
 
        ldata->erasing = 0;
        bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
                if (c == EOF_CHAR(tty)) {
                        if (read_cnt(ldata) >= N_TTY_BUF_SIZE)
                                return;
-                       if (ldata->canon_head != ldata->read_head)
-                               set_bit(TTY_PUSH, &tty->flags);
                        c = __DISABLED_CHAR;
                        goto handle_newline;
                }
                canon_change = (old->c_lflag ^ tty->termios.c_lflag) & ICANON;
        if (canon_change) {
                bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
+               ldata->line_start = 0;
                ldata->canon_head = ldata->read_tail;
                ldata->erasing = 0;
                ldata->lnext = 0;
        size_t eol;
        size_t tail;
        int ret, found = 0;
+       bool eof_push = 0;
 
        /* N.B. avoid overrun if nr == 0 */
        n = min(*nr, read_cnt(ldata));
        n = (found + eol + size) & (N_TTY_BUF_SIZE - 1);
        c = n;
 
-       if (found && read_buf(ldata, eol) == __DISABLED_CHAR)
+       if (found && read_buf(ldata, eol) == __DISABLED_CHAR) {
                n--;
+               eof_push = !n && ldata->read_tail != ldata->line_start;
+       }
 
        n_tty_trace("%s: eol:%zu found:%d n:%zu c:%zu size:%zu more:%zu\n",
                    __func__, eol, found, n, c, size, more);
        smp_mb__after_clear_bit();
        ldata->read_tail += c;
 
-       if (found)
+       if (found) {
+               ldata->line_start = ldata->read_tail;
                tty_audit_push(tty);
-       return 0;
+       }
+       return eof_push ? -EAGAIN : 0;
 }
 
 extern ssize_t redirected_tty_write(struct file *, const char __user *,
        int c;
        int minimum, time;
        ssize_t retval = 0;
-       ssize_t size;
        long timeout;
        unsigned long flags;
        int packet;
 
-do_it_again:
        c = job_control(tty, file);
        if (c < 0)
                return c;
 
                if (ldata->icanon && !L_EXTPROC(tty)) {
                        retval = canon_copy_from_read_buf(tty, &b, &nr);
-                       if (retval)
+                       if (retval == -EAGAIN) {
+                               retval = 0;
+                               continue;
+                       } else if (retval)
                                break;
                } else {
                        int uncopied;
                ldata->minimum_to_wake = minimum;
 
        __set_current_state(TASK_RUNNING);
-       size = b - buf;
-       if (size) {
-               retval = size;
-               if (nr)
-                       clear_bit(TTY_PUSH, &tty->flags);
-       } else if (test_and_clear_bit(TTY_PUSH, &tty->flags)) {
-               up_read(&tty->termios_rwsem);
-               goto do_it_again;
-       }
+       if (b - buf)
+               retval = b - buf;
 
        n_tty_set_room(tty);
        up_read(&tty->termios_rwsem);