aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Fulghum <paulkf@microgate.com>2006-02-10 04:51:14 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-02-10 11:13:12 -0500
commit8977d929e49021d9a6e031310aab01fa72f849c2 (patch)
tree13697e607b1153666139114242964f9982acc328
parentf0188f47482efdbd2e005103bb4f0224a835dfad (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>
-rw-r--r--drivers/char/tty_io.c30
-rw-r--r--include/linux/kbd_kern.h4
-rw-r--r--include/linux/tty.h2
-rw-r--r--include/linux/tty_flip.h4
4 files changed, 30 insertions, 10 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)
diff --git a/include/linux/kbd_kern.h b/include/linux/kbd_kern.h
index 3aed37314ab8..e87c32a5c86a 100644
--- a/include/linux/kbd_kern.h
+++ b/include/linux/kbd_kern.h
@@ -153,8 +153,10 @@ static inline void con_schedule_flip(struct tty_struct *t)
153{ 153{
154 unsigned long flags; 154 unsigned long flags;
155 spin_lock_irqsave(&t->buf.lock, flags); 155 spin_lock_irqsave(&t->buf.lock, flags);
156 if (t->buf.tail != NULL) 156 if (t->buf.tail != NULL) {
157 t->buf.tail->active = 0; 157 t->buf.tail->active = 0;
158 t->buf.tail->commit = t->buf.tail->used;
159 }
158 spin_unlock_irqrestore(&t->buf.lock, flags); 160 spin_unlock_irqrestore(&t->buf.lock, flags);
159 schedule_work(&t->buf.work); 161 schedule_work(&t->buf.work);
160} 162}
diff --git a/include/linux/tty.h b/include/linux/tty.h
index a7bd3b4558d2..f45cd74e6f24 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -58,6 +58,8 @@ struct tty_buffer {
58 int used; 58 int used;
59 int size; 59 int size;
60 int active; 60 int active;
61 int commit;
62 int read;
61 /* Data points here */ 63 /* Data points here */
62 unsigned long data[0]; 64 unsigned long data[0];
63}; 65};
diff --git a/include/linux/tty_flip.h b/include/linux/tty_flip.h
index 82961eb19888..222faf97d5f9 100644
--- a/include/linux/tty_flip.h
+++ b/include/linux/tty_flip.h
@@ -29,8 +29,10 @@ _INLINE_ void tty_schedule_flip(struct tty_struct *tty)
29{ 29{
30 unsigned long flags; 30 unsigned long flags;
31 spin_lock_irqsave(&tty->buf.lock, flags); 31 spin_lock_irqsave(&tty->buf.lock, flags);
32 if (tty->buf.tail != NULL) 32 if (tty->buf.tail != NULL) {
33 tty->buf.tail->active = 0; 33 tty->buf.tail->active = 0;
34 tty->buf.tail->commit = tty->buf.tail->used;
35 }
34 spin_unlock_irqrestore(&tty->buf.lock, flags); 36 spin_unlock_irqrestore(&tty->buf.lock, flags);
35 schedule_delayed_work(&tty->buf.work, 1); 37 schedule_delayed_work(&tty->buf.work, 1);
36} 38}