From 10967e5464496c90d21bdeec06ed74a6887b0bc1 Mon Sep 17 00:00:00 2001 From: "kfraser@localhost.localdomain" Date: Mon, 13 Nov 2006 09:46:05 +0000 Subject: [PATCH] [IOEMU] Fix Linux smp guest hangs with complaint "BUG: soft lock detected on CPU#0" When console=ttyS0 in guest grub configuration and serial='pty' in vmx configure file. The root cause to this bug is the characteristic of PTY emulator in Qemu. PTY has write/read end with some buffer. Write to a buffer will be failed after the buffer is full until read end reads the data from buffer. Previous to changeset 12026, write to serial port would fail quietly when buffer for pty is full. With changeset 12026, write to serial port would retry 3 times in 300ms if failed, even without notifying guest using serial irq. Smp guest will hang, waiting for an interrupt. For SMP guest, a watchdog thread should be executed periodically, otherwise soft lockup is detected. With this patch, an upper threshold of total consecutive retries is added and serial interrupt would be sent after retry 3 times for each write request, even if failed. Signed-off-by: Xinmei Huang Signed-off-by: Keir Fraser --- tools/ioemu/hw/serial.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/tools/ioemu/hw/serial.c b/tools/ioemu/hw/serial.c index c27a803a6e..b99e774eae 100644 --- a/tools/ioemu/hw/serial.c +++ b/tools/ioemu/hw/serial.c @@ -73,6 +73,11 @@ #define UART_LSR_OE 0x02 /* Overrun error indicator */ #define UART_LSR_DR 0x01 /* Receiver data ready */ +/* Maximum retries for a single byte transmit. */ +#define WRITE_MAX_SINGLE_RETRIES 3 +/* Maximum retries for a sequence of back-to-back unsuccessful transmits. */ +#define WRITE_MAX_TOTAL_RETRIES 10 + struct SerialState { uint8_t divider; uint8_t rbr; /* receive register */ @@ -98,8 +103,12 @@ struct SerialState { * If a character transmitted via UART cannot be written to its * destination immediately we remember it here and retry a few times via * a polling timer. + * - write_single_retries: Number of write retries for current byte. + * - write_total_retries: Number of write retries for back-to-back + * unsuccessful transmits. */ - int write_retries; + int write_single_retries; + int write_total_retries; char write_chr; QEMUTimer *write_retry_timer; }; @@ -217,16 +226,21 @@ static void serial_chr_write(void *opaque) { SerialState *s = opaque; + /* Cancel any outstanding retry if this is a new byte. */ qemu_del_timer(s->write_retry_timer); /* Retry every 100ms for 300ms total. */ if (qemu_chr_write(s->chr, &s->write_chr, 1) == -1) { - if (s->write_retries++ >= 3) - printf("serial: write error\n"); - else + s->write_total_retries++; + if (s->write_single_retries++ >= WRITE_MAX_SINGLE_RETRIES) + fprintf(stderr, "serial: write error\n"); + else if (s->write_total_retries <= WRITE_MAX_TOTAL_RETRIES) { qemu_mod_timer(s->write_retry_timer, qemu_get_clock(vm_clock) + ticks_per_sec / 10); - return; + return; + } + } else { + s->write_total_retries = 0; /* if successful then reset counter */ } /* Success: Notify guest that THR is empty. */ @@ -255,7 +269,7 @@ static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val) s->lsr &= ~UART_LSR_THRE; serial_update_irq(s); s->write_chr = val; - s->write_retries = 0; + s->write_single_retries = 0; serial_chr_write(s); } break; -- 2.30.2