diff options
author | Paul Fulghum <paulkf@microgate.com> | 2006-02-10 04:51:14 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-02-10 11:13:12 -0500 |
commit | 8977d929e49021d9a6e031310aab01fa72f849c2 (patch) | |
tree | 13697e607b1153666139114242964f9982acc328 /drivers/char/tty_io.c | |
parent | f0188f47482efdbd2e005103bb4f0224a835dfad (diff) |
[PATCH] tty buffering stall fix
Prevent stalled processing of received data when a driver allocates tty
buffer space but does not immediately follow the allocation with more data
and a call to schedule receive tty processing. (example: hvc_console) This
bug was introduced by the first locking patch for the new tty buffering.
Signed-off-by: Paul Fulghum <paulkf@microgate.com>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/char/tty_io.c')
-rw-r--r-- | drivers/char/tty_io.c | 30 |
1 files changed, 22 insertions, 8 deletions
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 076e07c1da38..a23816d3e9a1 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c | |||
@@ -268,6 +268,8 @@ static struct tty_buffer *tty_buffer_alloc(size_t size) | |||
268 | p->size = size; | 268 | p->size = size; |
269 | p->next = NULL; | 269 | p->next = NULL; |
270 | p->active = 0; | 270 | p->active = 0; |
271 | p->commit = 0; | ||
272 | p->read = 0; | ||
271 | p->char_buf_ptr = (char *)(p->data); | 273 | p->char_buf_ptr = (char *)(p->data); |
272 | p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size; | 274 | p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size; |
273 | /* printk("Flip create %p\n", p); */ | 275 | /* printk("Flip create %p\n", p); */ |
@@ -298,6 +300,8 @@ static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size) | |||
298 | *tbh = t->next; | 300 | *tbh = t->next; |
299 | t->next = NULL; | 301 | t->next = NULL; |
300 | t->used = 0; | 302 | t->used = 0; |
303 | t->commit = 0; | ||
304 | t->read = 0; | ||
301 | /* DEBUG ONLY */ | 305 | /* DEBUG ONLY */ |
302 | memset(t->data, '*', size); | 306 | memset(t->data, '*', size); |
303 | /* printk("Flip recycle %p\n", t); */ | 307 | /* printk("Flip recycle %p\n", t); */ |
@@ -335,6 +339,7 @@ int tty_buffer_request_room(struct tty_struct *tty, size_t size) | |||
335 | if (b != NULL) { | 339 | if (b != NULL) { |
336 | b->next = n; | 340 | b->next = n; |
337 | b->active = 0; | 341 | b->active = 0; |
342 | b->commit = b->used; | ||
338 | } else | 343 | } else |
339 | tty->buf.head = n; | 344 | tty->buf.head = n; |
340 | tty->buf.tail = n; | 345 | tty->buf.tail = n; |
@@ -2752,6 +2757,9 @@ static void flush_to_ldisc(void *private_) | |||
2752 | unsigned long flags; | 2757 | unsigned long flags; |
2753 | struct tty_ldisc *disc; | 2758 | struct tty_ldisc *disc; |
2754 | struct tty_buffer *tbuf; | 2759 | struct tty_buffer *tbuf; |
2760 | int count; | ||
2761 | char *char_buf; | ||
2762 | unsigned char *flag_buf; | ||
2755 | 2763 | ||
2756 | disc = tty_ldisc_ref(tty); | 2764 | disc = tty_ldisc_ref(tty); |
2757 | if (disc == NULL) /* !TTY_LDISC */ | 2765 | if (disc == NULL) /* !TTY_LDISC */ |
@@ -2765,16 +2773,20 @@ static void flush_to_ldisc(void *private_) | |||
2765 | goto out; | 2773 | goto out; |
2766 | } | 2774 | } |
2767 | spin_lock_irqsave(&tty->buf.lock, flags); | 2775 | spin_lock_irqsave(&tty->buf.lock, flags); |
2768 | while((tbuf = tty->buf.head) != NULL && !tbuf->active) { | 2776 | while((tbuf = tty->buf.head) != NULL) { |
2777 | while ((count = tbuf->commit - tbuf->read) != 0) { | ||
2778 | char_buf = tbuf->char_buf_ptr + tbuf->read; | ||
2779 | flag_buf = tbuf->flag_buf_ptr + tbuf->read; | ||
2780 | tbuf->read += count; | ||
2781 | spin_unlock_irqrestore(&tty->buf.lock, flags); | ||
2782 | disc->receive_buf(tty, char_buf, flag_buf, count); | ||
2783 | spin_lock_irqsave(&tty->buf.lock, flags); | ||
2784 | } | ||
2785 | if (tbuf->active) | ||
2786 | break; | ||
2769 | tty->buf.head = tbuf->next; | 2787 | tty->buf.head = tbuf->next; |
2770 | if (tty->buf.head == NULL) | 2788 | if (tty->buf.head == NULL) |
2771 | tty->buf.tail = NULL; | 2789 | tty->buf.tail = NULL; |
2772 | spin_unlock_irqrestore(&tty->buf.lock, flags); | ||
2773 | /* printk("Process buffer %p for %d\n", tbuf, tbuf->used); */ | ||
2774 | disc->receive_buf(tty, tbuf->char_buf_ptr, | ||
2775 | tbuf->flag_buf_ptr, | ||
2776 | tbuf->used); | ||
2777 | spin_lock_irqsave(&tty->buf.lock, flags); | ||
2778 | tty_buffer_free(tty, tbuf); | 2790 | tty_buffer_free(tty, tbuf); |
2779 | } | 2791 | } |
2780 | spin_unlock_irqrestore(&tty->buf.lock, flags); | 2792 | spin_unlock_irqrestore(&tty->buf.lock, flags); |
@@ -2871,8 +2883,10 @@ void tty_flip_buffer_push(struct tty_struct *tty) | |||
2871 | { | 2883 | { |
2872 | unsigned long flags; | 2884 | unsigned long flags; |
2873 | spin_lock_irqsave(&tty->buf.lock, flags); | 2885 | spin_lock_irqsave(&tty->buf.lock, flags); |
2874 | if (tty->buf.tail != NULL) | 2886 | if (tty->buf.tail != NULL) { |
2875 | tty->buf.tail->active = 0; | 2887 | tty->buf.tail->active = 0; |
2888 | tty->buf.tail->commit = tty->buf.tail->used; | ||
2889 | } | ||
2876 | spin_unlock_irqrestore(&tty->buf.lock, flags); | 2890 | spin_unlock_irqrestore(&tty->buf.lock, flags); |
2877 | 2891 | ||
2878 | if (tty->low_latency) | 2892 | if (tty->low_latency) |