Commit 8977d929 authored by Paul Fulghum's avatar Paul Fulghum Committed by Linus Torvalds

[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: default avatarPaul Fulghum <paulkf@microgate.com>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent f0188f47
...@@ -268,6 +268,8 @@ static struct tty_buffer *tty_buffer_alloc(size_t size) ...@@ -268,6 +268,8 @@ static struct tty_buffer *tty_buffer_alloc(size_t size)
p->size = size; p->size = size;
p->next = NULL; p->next = NULL;
p->active = 0; p->active = 0;
p->commit = 0;
p->read = 0;
p->char_buf_ptr = (char *)(p->data); p->char_buf_ptr = (char *)(p->data);
p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size; p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size;
/* printk("Flip create %p\n", p); */ /* 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,6 +300,8 @@ static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size)
*tbh = t->next; *tbh = t->next;
t->next = NULL; t->next = NULL;
t->used = 0; t->used = 0;
t->commit = 0;
t->read = 0;
/* DEBUG ONLY */ /* DEBUG ONLY */
memset(t->data, '*', size); memset(t->data, '*', size);
/* printk("Flip recycle %p\n", t); */ /* printk("Flip recycle %p\n", t); */
...@@ -335,6 +339,7 @@ int tty_buffer_request_room(struct tty_struct *tty, size_t size) ...@@ -335,6 +339,7 @@ int tty_buffer_request_room(struct tty_struct *tty, size_t size)
if (b != NULL) { if (b != NULL) {
b->next = n; b->next = n;
b->active = 0; b->active = 0;
b->commit = b->used;
} else } else
tty->buf.head = n; tty->buf.head = n;
tty->buf.tail = n; tty->buf.tail = n;
...@@ -2752,6 +2757,9 @@ static void flush_to_ldisc(void *private_) ...@@ -2752,6 +2757,9 @@ static void flush_to_ldisc(void *private_)
unsigned long flags; unsigned long flags;
struct tty_ldisc *disc; struct tty_ldisc *disc;
struct tty_buffer *tbuf; struct tty_buffer *tbuf;
int count;
char *char_buf;
unsigned char *flag_buf;
disc = tty_ldisc_ref(tty); disc = tty_ldisc_ref(tty);
if (disc == NULL) /* !TTY_LDISC */ if (disc == NULL) /* !TTY_LDISC */
...@@ -2765,16 +2773,20 @@ static void flush_to_ldisc(void *private_) ...@@ -2765,16 +2773,20 @@ static void flush_to_ldisc(void *private_)
goto out; goto out;
} }
spin_lock_irqsave(&tty->buf.lock, flags); spin_lock_irqsave(&tty->buf.lock, flags);
while((tbuf = tty->buf.head) != NULL && !tbuf->active) { while((tbuf = tty->buf.head) != NULL) {
while ((count = tbuf->commit - tbuf->read) != 0) {
char_buf = tbuf->char_buf_ptr + tbuf->read;
flag_buf = tbuf->flag_buf_ptr + tbuf->read;
tbuf->read += count;
spin_unlock_irqrestore(&tty->buf.lock, flags);
disc->receive_buf(tty, char_buf, flag_buf, count);
spin_lock_irqsave(&tty->buf.lock, flags);
}
if (tbuf->active)
break;
tty->buf.head = tbuf->next; tty->buf.head = tbuf->next;
if (tty->buf.head == NULL) if (tty->buf.head == NULL)
tty->buf.tail = NULL; tty->buf.tail = NULL;
spin_unlock_irqrestore(&tty->buf.lock, flags);
/* printk("Process buffer %p for %d\n", tbuf, tbuf->used); */
disc->receive_buf(tty, tbuf->char_buf_ptr,
tbuf->flag_buf_ptr,
tbuf->used);
spin_lock_irqsave(&tty->buf.lock, flags);
tty_buffer_free(tty, tbuf); tty_buffer_free(tty, tbuf);
} }
spin_unlock_irqrestore(&tty->buf.lock, flags); spin_unlock_irqrestore(&tty->buf.lock, flags);
...@@ -2871,8 +2883,10 @@ void tty_flip_buffer_push(struct tty_struct *tty) ...@@ -2871,8 +2883,10 @@ void tty_flip_buffer_push(struct tty_struct *tty)
{ {
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&tty->buf.lock, flags); spin_lock_irqsave(&tty->buf.lock, flags);
if (tty->buf.tail != NULL) if (tty->buf.tail != NULL) {
tty->buf.tail->active = 0; tty->buf.tail->active = 0;
tty->buf.tail->commit = tty->buf.tail->used;
}
spin_unlock_irqrestore(&tty->buf.lock, flags); spin_unlock_irqrestore(&tty->buf.lock, flags);
if (tty->low_latency) if (tty->low_latency)
......
...@@ -153,8 +153,10 @@ static inline void con_schedule_flip(struct tty_struct *t) ...@@ -153,8 +153,10 @@ static inline void con_schedule_flip(struct tty_struct *t)
{ {
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&t->buf.lock, flags); spin_lock_irqsave(&t->buf.lock, flags);
if (t->buf.tail != NULL) if (t->buf.tail != NULL) {
t->buf.tail->active = 0; t->buf.tail->active = 0;
t->buf.tail->commit = t->buf.tail->used;
}
spin_unlock_irqrestore(&t->buf.lock, flags); spin_unlock_irqrestore(&t->buf.lock, flags);
schedule_work(&t->buf.work); schedule_work(&t->buf.work);
} }
......
...@@ -58,6 +58,8 @@ struct tty_buffer { ...@@ -58,6 +58,8 @@ struct tty_buffer {
int used; int used;
int size; int size;
int active; int active;
int commit;
int read;
/* Data points here */ /* Data points here */
unsigned long data[0]; unsigned long data[0];
}; };
......
...@@ -29,8 +29,10 @@ _INLINE_ void tty_schedule_flip(struct tty_struct *tty) ...@@ -29,8 +29,10 @@ _INLINE_ void tty_schedule_flip(struct tty_struct *tty)
{ {
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&tty->buf.lock, flags); spin_lock_irqsave(&tty->buf.lock, flags);
if (tty->buf.tail != NULL) if (tty->buf.tail != NULL) {
tty->buf.tail->active = 0; tty->buf.tail->active = 0;
tty->buf.tail->commit = tty->buf.tail->used;
}
spin_unlock_irqrestore(&tty->buf.lock, flags); spin_unlock_irqrestore(&tty->buf.lock, flags);
schedule_delayed_work(&tty->buf.work, 1); schedule_delayed_work(&tty->buf.work, 1);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment