From 3feebbb5492e9e463467cefb633e23a3dfcec132 Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Mon, 13 Oct 2008 23:12:50 +0000 Subject: 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 Tested-by: Christian Borntraeger (virtio_console) Signed-off-by: Hendrik Brueckner Signed-off-by: Benjamin Herrenschmidt --- drivers/char/hvc_console.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index c9d16665c96..3650c904401 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, } } else { r = cons_ops[index]->put_chars(vtermnos[index], c, i); - if (r < 0) { + if (r <= 0) { /* throw away chars on error */ i = 0; } else if (r > 0) { @@ -431,7 +431,7 @@ static void hvc_hangup(struct tty_struct *tty) * Push buffered characters whether they were just recently buffered or waiting * on a blocked hypervisor. Call this function with hp->lock held. */ -static void hvc_push(struct hvc_struct *hp) +static int hvc_push(struct hvc_struct *hp) { int n; @@ -439,7 +439,7 @@ static void hvc_push(struct hvc_struct *hp) if (n <= 0) { if (n == 0) { hp->do_wakeup = 1; - return; + return 0; } /* throw away output on error; this happens when there is no session connected to the vterm. */ @@ -450,6 +450,8 @@ static void hvc_push(struct hvc_struct *hp) memmove(hp->outbuf, hp->outbuf + n, hp->n_outbuf); else hp->do_wakeup = 1; + + return n; } 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) char buf[N_INBUF] __ALIGNED__; unsigned long flags; int read_total = 0; + int written_total = 0; spin_lock_irqsave(&hp->lock, flags); /* Push pending writes */ if (hp->n_outbuf > 0) - hvc_push(hp); + written_total = hvc_push(hp); /* Reschedule us if still some write pending */ - if (hp->n_outbuf > 0) + if (hp->n_outbuf > 0) { poll_mask |= HVC_POLL_WRITE; + /* If hvc_push() was not able to write, sleep a few msecs */ + timeout = (written_total) ? 0 : MIN_TIMEOUT; + } /* No tty attached, just skip */ tty = hp->tty; @@ -659,10 +665,6 @@ static int khvcd(void *unused) poll_mask |= HVC_POLL_READ; if (hvc_kicked) continue; - if (poll_mask & HVC_POLL_WRITE) { - yield(); - continue; - } set_current_state(TASK_INTERRUPTIBLE); if (!hvc_kicked) { if (poll_mask == 0) -- cgit v1.2.3