summaryrefslogtreecommitdiffstats
path: root/drivers/tty/n_tty.c
diff options
context:
space:
mode:
authorPeter Hurley2013-06-15 16:21:17 +0200
committerGreg Kroah-Hartman2013-07-24 02:08:40 +0200
commit40d5e0905a03601d40cd4e46b8690093c2355d03 (patch)
tree6562e24794811eb13d986e4ea90a55c82cbdae26 /drivers/tty/n_tty.c
parentn_tty: Avoid false-sharing echo buffer indices (diff)
downloadkernel-qcow2-linux-40d5e0905a03601d40cd4e46b8690093c2355d03.tar.gz
kernel-qcow2-linux-40d5e0905a03601d40cd4e46b8690093c2355d03.tar.xz
kernel-qcow2-linux-40d5e0905a03601d40cd4e46b8690093c2355d03.zip
n_tty: Fix EOF push handling
In canonical mode, an EOF which is not the first character of the line causes read() to complete and return the number of characters read so far (commonly referred to as EOF push). However, if the previous read() returned because the user buffer was full _and_ the next character is an EOF not at the beginning of the line, read() must not return 0, thus mistakenly indicating the end-of-file condition. The TTY_PUSH flag is used to indicate an EOF was received which is not at the beginning of the line. Because the EOF push condition is evaluated by a thread other than the read(), multiple EOF pushes can cause a premature end-of-file to be indicated. Instead, discover the 'EOF push as first read character' condition from the read() thread itself, and restart the i/o loop if detected. Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/n_tty.c')
-rw-r--r--drivers/tty/n_tty.c34
1 files changed, 17 insertions, 17 deletions
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index d4d71350a71d..25aaf1afa221 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -113,6 +113,7 @@ struct n_tty_data {
/* consumer-published */
size_t read_tail;
+ size_t line_start;
/* protected by output lock */
unsigned int column;
@@ -337,6 +338,7 @@ static void reset_buffer_flags(struct n_tty_data *ldata)
{
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);
@@ -1396,8 +1398,6 @@ send_signal:
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;
}
@@ -1604,6 +1604,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
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;
@@ -1837,6 +1838,7 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
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));
@@ -1863,8 +1865,10 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
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);
@@ -1887,9 +1891,11 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
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 *,
@@ -1964,12 +1970,10 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
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;
@@ -2076,7 +2080,10 @@ do_it_again:
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;
@@ -2104,15 +2111,8 @@ do_it_again:
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);