Skip to content

Commit

Permalink
tty: Fix pty master read() after slave closes
Browse files Browse the repository at this point in the history
Commit f95499c,
  n_tty: Don't wait for buffer work in read() loop
creates a race window which can cause a pty master read()
to miss the last pty slave write(s) and return -EIO instead,
thus signalling the pty slave is closed. This can happen when
the pty slave is written and immediately closed but before the
tty buffer i/o loop receives the new input; the pty master
read() is scheduled, sees its read buffer is empty and the
pty slave has been closed, and exits.

Because tty_flush_to_ldisc() has significant performance impact
for parallel i/o, rather than revert the commit, special case this
condition (ie., when the read buffer is empty and the 'other' pty
has been closed) and, only then, wait for buffer work to complete
before re-testing if the read buffer is still empty.

As before, subsequent pty master reads return any available data
until no more data is available, and then returns -EIO to
indicate the pty slave has closed.

Reported-by: Mikael Pettersson <mikpelinux@gmail.com>
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Tested-by: Mikael Pettersson <mikpelinux@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Peter Hurley authored and Greg Kroah-Hartman committed Oct 1, 2013
1 parent 15c03dd commit f8747d4
Showing 1 changed file with 26 additions and 20 deletions.
46 changes: 26 additions & 20 deletions drivers/tty/n_tty.c
Original file line number Diff line number Diff line change
Expand Up @@ -2183,28 +2183,34 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,

if (!input_available_p(tty, 0)) {
if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
retval = -EIO;
break;
}
if (tty_hung_up_p(file))
break;
if (!timeout)
break;
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
break;
}
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
n_tty_set_room(tty);
up_read(&tty->termios_rwsem);
up_read(&tty->termios_rwsem);
tty_flush_to_ldisc(tty);
down_read(&tty->termios_rwsem);
if (!input_available_p(tty, 0)) {
retval = -EIO;
break;
}
} else {
if (tty_hung_up_p(file))
break;
if (!timeout)
break;
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
break;
}
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
n_tty_set_room(tty);
up_read(&tty->termios_rwsem);

timeout = schedule_timeout(timeout);
timeout = schedule_timeout(timeout);

down_read(&tty->termios_rwsem);
continue;
down_read(&tty->termios_rwsem);
continue;
}
}
__set_current_state(TASK_RUNNING);

Expand Down

0 comments on commit f8747d4

Please sign in to comment.