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>
This commit is contained in:
parent
56ce9e2b24
commit
3feebbb549
1 changed files with 11 additions and 9 deletions
|
@ -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)
|
||||||
* 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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
|
|
Loading…
Reference in a new issue