diff options
author | Paul Fulghum <paulkf@microgate.com> | 2006-06-28 15:26:48 +0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-06-29 01:59:05 +0400 |
commit | 2c3bb20f46709a0adfa7ea408013edbcab945d5a (patch) | |
tree | 951dde0a4ea31892635886afb1e3f0f03847b1f0 /drivers/char | |
parent | 817d6d3bceaf34c99f5343820f9b9e6021f0655c (diff) | |
download | linux-2c3bb20f46709a0adfa7ea408013edbcab945d5a.tar.xz |
[PATCH] add receive_room flow control to flush_to_ldisc
Flush data serially to line discipline in blocks no larger than
tty->receive_room to avoid losing data if line discipline is busy (such as
N_TTY operating at high speed on heavily loaded system) or does not accept
data in large blocks (such as N_MOUSE).
Signed-off-by: Paul Fulghum <paulkf@microgate.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/tty_io.c | 37 |
1 files changed, 24 insertions, 13 deletions
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index b846d87f2b56..1f03ebf165d9 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -2771,8 +2771,7 @@ static void flush_to_ldisc(void *private_) struct tty_struct *tty = (struct tty_struct *) private_; unsigned long flags; struct tty_ldisc *disc; - struct tty_buffer *tbuf; - int count; + struct tty_buffer *tbuf, *head; char *char_buf; unsigned char *flag_buf; @@ -2781,21 +2780,33 @@ static void flush_to_ldisc(void *private_) return; spin_lock_irqsave(&tty->buf.lock, flags); - while((tbuf = tty->buf.head) != NULL) { - while ((count = tbuf->commit - tbuf->read) != 0) { - char_buf = tbuf->char_buf_ptr + tbuf->read; - flag_buf = tbuf->flag_buf_ptr + tbuf->read; - tbuf->read += count; + head = tty->buf.head; + if (head != NULL) { + tty->buf.head = NULL; + for (;;) { + int count = head->commit - head->read; + if (!count) { + if (head->next == NULL) + break; + tbuf = head; + head = head->next; + tty_buffer_free(tty, tbuf); + continue; + } + if (!tty->receive_room) { + schedule_delayed_work(&tty->buf.work, 1); + break; + } + if (count > tty->receive_room) + count = tty->receive_room; + char_buf = head->char_buf_ptr + head->read; + flag_buf = head->flag_buf_ptr + head->read; + head->read += count; spin_unlock_irqrestore(&tty->buf.lock, flags); disc->receive_buf(tty, char_buf, flag_buf, count); spin_lock_irqsave(&tty->buf.lock, flags); } - if (tbuf->active) - break; - tty->buf.head = tbuf->next; - if (tty->buf.head == NULL) - tty->buf.tail = NULL; - tty_buffer_free(tty, tbuf); + tty->buf.head = head; } spin_unlock_irqrestore(&tty->buf.lock, flags); |