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 | } |
