Commit 3feebbb5 authored by Hendrik Brueckner's avatar Hendrik Brueckner Committed by Benjamin Herrenschmidt

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: default avatarChristian Borntraeger <borntraeger@de.ibm.com>
Tested-by: Christian Borntraeger <borntraeger@de.ibm.com> (virtio_console)
Signed-off-by: default avatarHendrik Brueckner <brueckner@linux.vnet.ibm.com>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent 56ce9e2b
...@@ -161,7 +161,7 @@ static void hvc_console_print(struct console *co, const char *b, ...@@ -161,7 +161,7 @@ static void hvc_console_print(struct console *co, const char *b,
} }
} else { } else {
r = cons_ops[index]->put_chars(vtermnos[index], c, i); r = cons_ops[index]->put_chars(vtermnos[index], c, i);
if (r < 0) { if (r <= 0) {
/* throw away chars on error */ /* throw away chars on error */
i = 0; i = 0;
} else if (r > 0) { } else if (r > 0) {
...@@ -431,7 +431,7 @@ static void hvc_hangup(struct tty_struct *tty) ...@@ -431,7 +431,7 @@ static void hvc_hangup(struct tty_struct *tty)
* Push buffered characters whether they were just recently buffered or waiting * Push buffered characters whether they were just recently buffered or waiting
* on a blocked hypervisor. Call this function with hp->lock held. * 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; int n;
...@@ -439,7 +439,7 @@ static void hvc_push(struct hvc_struct *hp) ...@@ -439,7 +439,7 @@ static void hvc_push(struct hvc_struct *hp)
if (n <= 0) { if (n <= 0) {
if (n == 0) { if (n == 0) {
hp->do_wakeup = 1; hp->do_wakeup = 1;
return; return 0;
} }
/* throw away output on error; this happens when /* throw away output on error; this happens when
there is no session connected to the vterm. */ there is no session connected to the vterm. */
...@@ -450,6 +450,8 @@ static void hvc_push(struct hvc_struct *hp) ...@@ -450,6 +450,8 @@ static void hvc_push(struct hvc_struct *hp)
memmove(hp->outbuf, hp->outbuf + n, hp->n_outbuf); memmove(hp->outbuf, hp->outbuf + n, hp->n_outbuf);
else else
hp->do_wakeup = 1; hp->do_wakeup = 1;
return n;
} }
static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count) 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,16 +540,20 @@ int hvc_poll(struct hvc_struct *hp)
char buf[N_INBUF] __ALIGNED__; char buf[N_INBUF] __ALIGNED__;
unsigned long flags; unsigned long flags;
int read_total = 0; int read_total = 0;
int written_total = 0;
spin_lock_irqsave(&hp->lock, flags); spin_lock_irqsave(&hp->lock, flags);
/* Push pending writes */ /* Push pending writes */
if (hp->n_outbuf > 0) if (hp->n_outbuf > 0)
hvc_push(hp); written_total = hvc_push(hp);
/* Reschedule us if still some write pending */ /* Reschedule us if still some write pending */
if (hp->n_outbuf > 0) if (hp->n_outbuf > 0) {
poll_mask |= HVC_POLL_WRITE; 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 */ /* No tty attached, just skip */
tty = hp->tty; tty = hp->tty;
...@@ -659,10 +665,6 @@ static int khvcd(void *unused) ...@@ -659,10 +665,6 @@ static int khvcd(void *unused)
poll_mask |= HVC_POLL_READ; poll_mask |= HVC_POLL_READ;
if (hvc_kicked) if (hvc_kicked)
continue; continue;
if (poll_mask & HVC_POLL_WRITE) {
yield();
continue;
}
set_current_state(TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
if (!hvc_kicked) { if (!hvc_kicked) {
if (poll_mask == 0) if (poll_mask == 0)
......
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