Commit 8977d929e49021d9a6e031310aab01fa72f849c2

Authored by Paul Fulghum
Committed by Linus Torvalds
1 parent f0188f4748

[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 }
... ... @@ -58,6 +58,8 @@
58 58 int used;
59 59 int size;
60 60 int active;
  61 + int commit;
  62 + int read;
61 63 /* Data points here */
62 64 unsigned long data[0];
63 65 };
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 }