aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorPaul Fulghum <paulkf@microgate.com>2006-06-28 04:26:48 -0700
committerLinus Torvalds <torvalds@g5.osdl.org>2006-06-28 14:59:05 -0700
commit2c3bb20f46709a0adfa7ea408013edbcab945d5a (patch)
tree951dde0a4ea31892635886afb1e3f0f03847b1f0 /drivers
parent817d6d3bceaf34c99f5343820f9b9e6021f0655c (diff)
downloadlinux-2c3bb20f46709a0adfa7ea408013edbcab945d5a.tar.gz
[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')
-rw-r--r--drivers/char/tty_io.c37
1 files changed, 24 insertions, 13 deletions
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index b846d87f2b56a..1f03ebf165d90 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);