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 | |
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>
-rw-r--r-- | drivers/char/tty_io.c | 30 | ||||
-rw-r--r-- | include/linux/kbd_kern.h | 4 | ||||
-rw-r--r-- | include/linux/tty.h | 2 | ||||
-rw-r--r-- | include/linux/tty_flip.h | 4 |
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 | } |