Commit 8977d929e49021d9a6e031310aab01fa72f849c2
Committed by
Linus Torvalds
1 parent
f0188f4748
Exists in
master
and in
7 other branches
[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>
Showing 4 changed files with 30 additions and 10 deletions Side-by-side Diff
drivers/char/tty_io.c
... | ... | @@ -268,6 +268,8 @@ |
268 | 268 | p->size = size; |
269 | 269 | p->next = NULL; |
270 | 270 | p->active = 0; |
271 | + p->commit = 0; | |
272 | + p->read = 0; | |
271 | 273 | p->char_buf_ptr = (char *)(p->data); |
272 | 274 | p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size; |
273 | 275 | /* printk("Flip create %p\n", p); */ |
... | ... | @@ -298,6 +300,8 @@ |
298 | 300 | *tbh = t->next; |
299 | 301 | t->next = NULL; |
300 | 302 | t->used = 0; |
303 | + t->commit = 0; | |
304 | + t->read = 0; | |
301 | 305 | /* DEBUG ONLY */ |
302 | 306 | memset(t->data, '*', size); |
303 | 307 | /* printk("Flip recycle %p\n", t); */ |
... | ... | @@ -335,6 +339,7 @@ |
335 | 339 | if (b != NULL) { |
336 | 340 | b->next = n; |
337 | 341 | b->active = 0; |
342 | + b->commit = b->used; | |
338 | 343 | } else |
339 | 344 | tty->buf.head = n; |
340 | 345 | tty->buf.tail = n; |
... | ... | @@ -2752,6 +2757,9 @@ |
2752 | 2757 | unsigned long flags; |
2753 | 2758 | struct tty_ldisc *disc; |
2754 | 2759 | struct tty_buffer *tbuf; |
2760 | + int count; | |
2761 | + char *char_buf; | |
2762 | + unsigned char *flag_buf; | |
2755 | 2763 | |
2756 | 2764 | disc = tty_ldisc_ref(tty); |
2757 | 2765 | if (disc == NULL) /* !TTY_LDISC */ |
2758 | 2766 | |
... | ... | @@ -2765,16 +2773,20 @@ |
2765 | 2773 | goto out; |
2766 | 2774 | } |
2767 | 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 | 2787 | tty->buf.head = tbuf->next; |
2770 | 2788 | if (tty->buf.head == NULL) |
2771 | 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 | 2790 | tty_buffer_free(tty, tbuf); |
2779 | 2791 | } |
2780 | 2792 | spin_unlock_irqrestore(&tty->buf.lock, flags); |
2781 | 2793 | |
... | ... | @@ -2871,8 +2883,10 @@ |
2871 | 2883 | { |
2872 | 2884 | unsigned long flags; |
2873 | 2885 | spin_lock_irqsave(&tty->buf.lock, flags); |
2874 | - if (tty->buf.tail != NULL) | |
2886 | + if (tty->buf.tail != NULL) { | |
2875 | 2887 | tty->buf.tail->active = 0; |
2888 | + tty->buf.tail->commit = tty->buf.tail->used; | |
2889 | + } | |
2876 | 2890 | spin_unlock_irqrestore(&tty->buf.lock, flags); |
2877 | 2891 | |
2878 | 2892 | if (tty->low_latency) |
include/linux/kbd_kern.h
... | ... | @@ -153,8 +153,10 @@ |
153 | 153 | { |
154 | 154 | unsigned long flags; |
155 | 155 | spin_lock_irqsave(&t->buf.lock, flags); |
156 | - if (t->buf.tail != NULL) | |
156 | + if (t->buf.tail != NULL) { | |
157 | 157 | t->buf.tail->active = 0; |
158 | + t->buf.tail->commit = t->buf.tail->used; | |
159 | + } | |
158 | 160 | spin_unlock_irqrestore(&t->buf.lock, flags); |
159 | 161 | schedule_work(&t->buf.work); |
160 | 162 | } |
include/linux/tty.h
include/linux/tty_flip.h
... | ... | @@ -29,8 +29,10 @@ |
29 | 29 | { |
30 | 30 | unsigned long flags; |
31 | 31 | spin_lock_irqsave(&tty->buf.lock, flags); |
32 | - if (tty->buf.tail != NULL) | |
32 | + if (tty->buf.tail != NULL) { | |
33 | 33 | tty->buf.tail->active = 0; |
34 | + tty->buf.tail->commit = tty->buf.tail->used; | |
35 | + } | |
34 | 36 | spin_unlock_irqrestore(&tty->buf.lock, flags); |
35 | 37 | schedule_delayed_work(&tty->buf.work, 1); |
36 | 38 | } |