diff options
author | Hendrik Brueckner <brueckner@linux.vnet.ibm.com> | 2008-10-13 19:12:50 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2008-10-21 19:59:55 -0400 |
commit | 3feebbb5492e9e463467cefb633e23a3dfcec132 (patch) | |
tree | aab5c73bbcd799781b8b27f7173c3e726ea403be /drivers/char/hvc_console.c | |
parent | 56ce9e2b2463b3b257098aa966586701d9967f1e (diff) |
hvc_console: Fix loop if put_char() returns 0
If put_char() routine of a hvc console backend returns 0, then the
hvc console starts looping in the following scenarios:
1. hvc_console_print()
If put_char() returns 0 then the while loop may loop forever.
I have added the missing check for 0 to throw away console messages.
2. khvcd may loop:
The thread calls hvc_poll() --> hvc_push()... if there are still
buffered data then the HVC_POLL_WRITE bit is set and causes the
khvcd thread to loop (if yield() returns immediately).
However, instead of looping, the khvcd thread could sleep for
MIN_TIMEOUT (doing the same as for get_chars()).
The MIN_TIMEOUT is set if hvc_push() was not able to write
data to the backend. If data has been written, the timeout is
set to 0 to immediately re-schedule hvc_poll().
Reviewed-by: Christian Borntraeger <borntraeger@de.ibm.com>
Tested-by: Christian Borntraeger <borntraeger@de.ibm.com> (virtio_console)
Signed-off-by: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'drivers/char/hvc_console.c')
-rw-r--r-- | drivers/char/hvc_console.c | 20 |
1 files changed, 11 insertions, 9 deletions
diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index c9d16665c960..3650c904401e 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c | |||
@@ -161,7 +161,7 @@ static void hvc_console_print(struct console *co, const char *b, | |||
161 | } | 161 | } |
162 | } else { | 162 | } else { |
163 | r = cons_ops[index]->put_chars(vtermnos[index], c, i); | 163 | r = cons_ops[index]->put_chars(vtermnos[index], c, i); |
164 | if (r < 0) { | 164 | if (r <= 0) { |
165 | /* throw away chars on error */ | 165 | /* throw away chars on error */ |
166 | i = 0; | 166 | i = 0; |
167 | } else if (r > 0) { | 167 | } else if (r > 0) { |
@@ -431,7 +431,7 @@ static void hvc_hangup(struct tty_struct *tty) | |||
431 | * Push buffered characters whether they were just recently buffered or waiting | 431 | * Push buffered characters whether they were just recently buffered or waiting |
432 | * on a blocked hypervisor. Call this function with hp->lock held. | 432 | * on a blocked hypervisor. Call this function with hp->lock held. |
433 | */ | 433 | */ |
434 | static void hvc_push(struct hvc_struct *hp) | 434 | static int hvc_push(struct hvc_struct *hp) |
435 | { | 435 | { |
436 | int n; | 436 | int n; |
437 | 437 | ||
@@ -439,7 +439,7 @@ static void hvc_push(struct hvc_struct *hp) | |||
439 | if (n <= 0) { | 439 | if (n <= 0) { |
440 | if (n == 0) { | 440 | if (n == 0) { |
441 | hp->do_wakeup = 1; | 441 | hp->do_wakeup = 1; |
442 | return; | 442 | return 0; |
443 | } | 443 | } |
444 | /* throw away output on error; this happens when | 444 | /* throw away output on error; this happens when |
445 | there is no session connected to the vterm. */ | 445 | there is no session connected to the vterm. */ |
@@ -450,6 +450,8 @@ static void hvc_push(struct hvc_struct *hp) | |||
450 | memmove(hp->outbuf, hp->outbuf + n, hp->n_outbuf); | 450 | memmove(hp->outbuf, hp->outbuf + n, hp->n_outbuf); |
451 | else | 451 | else |
452 | hp->do_wakeup = 1; | 452 | hp->do_wakeup = 1; |
453 | |||
454 | return n; | ||
453 | } | 455 | } |
454 | 456 | ||
455 | static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count) | 457 | static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count) |
@@ -538,16 +540,20 @@ int hvc_poll(struct hvc_struct *hp) | |||
538 | char buf[N_INBUF] __ALIGNED__; | 540 | char buf[N_INBUF] __ALIGNED__; |
539 | unsigned long flags; | 541 | unsigned long flags; |
540 | int read_total = 0; | 542 | int read_total = 0; |
543 | int written_total = 0; | ||
541 | 544 | ||
542 | spin_lock_irqsave(&hp->lock, flags); | 545 | spin_lock_irqsave(&hp->lock, flags); |
543 | 546 | ||
544 | /* Push pending writes */ | 547 | /* Push pending writes */ |
545 | if (hp->n_outbuf > 0) | 548 | if (hp->n_outbuf > 0) |
546 | hvc_push(hp); | 549 | written_total = hvc_push(hp); |
547 | 550 | ||
548 | /* Reschedule us if still some write pending */ | 551 | /* Reschedule us if still some write pending */ |
549 | if (hp->n_outbuf > 0) | 552 | if (hp->n_outbuf > 0) { |
550 | poll_mask |= HVC_POLL_WRITE; | 553 | poll_mask |= HVC_POLL_WRITE; |
554 | /* If hvc_push() was not able to write, sleep a few msecs */ | ||
555 | timeout = (written_total) ? 0 : MIN_TIMEOUT; | ||
556 | } | ||
551 | 557 | ||
552 | /* No tty attached, just skip */ | 558 | /* No tty attached, just skip */ |
553 | tty = hp->tty; | 559 | tty = hp->tty; |
@@ -659,10 +665,6 @@ static int khvcd(void *unused) | |||
659 | poll_mask |= HVC_POLL_READ; | 665 | poll_mask |= HVC_POLL_READ; |
660 | if (hvc_kicked) | 666 | if (hvc_kicked) |
661 | continue; | 667 | continue; |
662 | if (poll_mask & HVC_POLL_WRITE) { | ||
663 | yield(); | ||
664 | continue; | ||
665 | } | ||
666 | set_current_state(TASK_INTERRUPTIBLE); | 668 | set_current_state(TASK_INTERRUPTIBLE); |
667 | if (!hvc_kicked) { | 669 | if (!hvc_kicked) { |
668 | if (poll_mask == 0) | 670 | if (poll_mask == 0) |