From 252f3af86eaa15b522dffa6fdfa1d81fe8851c94 Mon Sep 17 00:00:00 2001 From: Justin M. Forbes Date: Mar 28 2011 18:54:26 +0000 Subject: Spice fixes for flow control --- diff --git a/0005-char-Split-out-tcp-socket-close-code-in-a-separate-f.patch b/0005-char-Split-out-tcp-socket-close-code-in-a-separate-f.patch new file mode 100644 index 0000000..0ec25a0 --- /dev/null +++ b/0005-char-Split-out-tcp-socket-close-code-in-a-separate-f.patch @@ -0,0 +1,56 @@ +>From b248befcd93bcd713971b15147fcaa217a3d1bb7 Mon Sep 17 00:00:00 2001 +From: Amit Shah +Date: Mon, 21 Mar 2011 21:57:47 +0100 +Subject: [PATCH 05/17] char: Split out tcp socket close code in a separate function + +Signed-off-by: Amit Shah +--- + qemu-char.c | 25 ++++++++++++++++--------- + 1 files changed, 16 insertions(+), 9 deletions(-) + +diff --git a/qemu-char.c b/qemu-char.c +index bd4e944..4b57af9 100644 +--- a/qemu-char.c ++++ b/qemu-char.c +@@ -1911,6 +1911,21 @@ typedef struct { + + static void tcp_chr_accept(void *opaque); + ++static void tcp_closed(void *opaque) ++{ ++ CharDriverState *chr = opaque; ++ TCPCharDriver *s = chr->opaque; ++ ++ s->connected = 0; ++ if (s->listen_fd >= 0) { ++ qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr); ++ } ++ qemu_set_fd_handler(s->fd, NULL, NULL, NULL); ++ closesocket(s->fd); ++ s->fd = -1; ++ qemu_chr_event(chr, CHR_EVENT_CLOSED); ++} ++ + static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) + { + TCPCharDriver *s = chr->opaque; +@@ -2061,15 +2076,7 @@ static void tcp_chr_read(void *opaque) + len = s->max_size; + size = tcp_chr_recv(chr, (void *)buf, len); + if (size == 0) { +- /* connection closed */ +- s->connected = 0; +- if (s->listen_fd >= 0) { +- qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr); +- } +- qemu_set_fd_handler(s->fd, NULL, NULL, NULL); +- closesocket(s->fd); +- s->fd = -1; +- qemu_chr_event(chr, CHR_EVENT_CLOSED); ++ tcp_closed(chr); + } else if (size > 0) { + if (s->do_telnetopt) + tcp_chr_process_IAC_bytes(chr, s, buf, &size); +-- +1.7.3.2 + diff --git a/0006-char-Add-a-QemuChrHandlers-struct-to-initialise-char.patch b/0006-char-Add-a-QemuChrHandlers-struct-to-initialise-char.patch new file mode 100644 index 0000000..095e7ff --- /dev/null +++ b/0006-char-Add-a-QemuChrHandlers-struct-to-initialise-char.patch @@ -0,0 +1,673 @@ +>From 003cc09f8fc34e7571ebd4a89ea6aa6324a80b54 Mon Sep 17 00:00:00 2001 +From: Amit Shah +Date: Mon, 21 Mar 2011 20:31:45 +0100 +Subject: [PATCH 06/19] char: Add a QemuChrHandlers struct to initialise chardev handlers + +Instead of passing each handler in the qemu_add_handlers() function, +create a struct of handlers that can be passed to the function instead. + +Signed-off-by: Amit Shah +--- + gdbstub.c | 9 +++++++-- + hw/debugcon.c | 2 +- + hw/escc.c | 9 +++++++-- + hw/etraxfs_ser.c | 13 +++++++++---- + hw/grlib_apbuart.c | 12 +++++++----- + hw/ivshmem.c | 28 ++++++++++++++++++++++------ + hw/mcf_uart.c | 9 +++++++-- + hw/pl011.c | 9 +++++++-- + hw/pxa2xx.c | 13 +++++++++---- + hw/serial.c | 9 +++++++-- + hw/sh_serial.c | 12 +++++++++--- + hw/syborg_serial.c | 9 +++++++-- + hw/usb-serial.c | 9 +++++++-- + hw/virtio-console.c | 9 +++++++-- + hw/xen_console.c | 16 +++++++++++----- + hw/xilinx_uartlite.c | 11 +++++++++-- + monitor.c | 18 ++++++++++++++---- + net/slirp.c | 8 ++++++-- + qemu-char.c | 30 +++++++++++++++++++++--------- + qemu-char.h | 13 +++++++++---- + 20 files changed, 183 insertions(+), 65 deletions(-) + +diff --git a/gdbstub.c b/gdbstub.c +index 14e8b9b..4190ac7 100644 +--- a/gdbstub.c ++++ b/gdbstub.c +@@ -2634,6 +2634,12 @@ static void gdb_sigterm_handler(int signal) + } + #endif + ++static const QemuChrHandlers gdb_handlers = { ++ .fd_can_read = gdb_chr_can_receive, ++ .fd_read = gdb_chr_receive, ++ .fd_event = gdb_chr_event, ++}; ++ + int gdbserver_start(const char *device) + { + GDBState *s; +@@ -2663,8 +2669,7 @@ int gdbserver_start(const char *device) + if (!chr) + return -1; + +- qemu_chr_add_handlers(chr, gdb_chr_can_receive, gdb_chr_receive, +- gdb_chr_event, NULL); ++ qemu_chr_add_handlers(chr, &gdb_handlers, NULL); + } + + s = gdbserver_state; +diff --git a/hw/debugcon.c b/hw/debugcon.c +index 5ee6821..e79a595 100644 +--- a/hw/debugcon.c ++++ b/hw/debugcon.c +@@ -73,7 +73,7 @@ static void debugcon_init_core(DebugconState *s) + exit(1); + } + +- qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, s); ++ qemu_chr_add_handlers(s->chr, NULL, s); + } + + static int debugcon_isa_initfn(ISADevice *dev) +diff --git a/hw/escc.c b/hw/escc.c +index f6fd919..dfa329a 100644 +--- a/hw/escc.c ++++ b/hw/escc.c +@@ -898,6 +898,12 @@ void slavio_serial_ms_kbd_init(target_phys_addr_t base, qemu_irq irq, + sysbus_mmio_map(s, 0, base); + } + ++static const QemuChrHandlers serial_handlers = { ++ .fd_can_read = serial_can_receive, ++ .fd_read = serial_receive1, ++ .fd_event = serial_event, ++}; ++ + static int escc_init1(SysBusDevice *dev) + { + SerialState *s = FROM_SYSBUS(SerialState, dev); +@@ -911,8 +917,7 @@ static int escc_init1(SysBusDevice *dev) + s->chn[i].chn = 1 - i; + s->chn[i].clock = s->frequency / 2; + if (s->chn[i].chr) { +- qemu_chr_add_handlers(s->chn[i].chr, serial_can_receive, +- serial_receive1, serial_event, &s->chn[i]); ++ qemu_chr_add_handlers(s->chn[i].chr, &serial_handlers, &s->chn[i]); + } + } + s->chn[0].otherchn = &s->chn[1]; +diff --git a/hw/etraxfs_ser.c b/hw/etraxfs_ser.c +index 2787ebd..406121c 100644 +--- a/hw/etraxfs_ser.c ++++ b/hw/etraxfs_ser.c +@@ -190,6 +190,12 @@ static void serial_event(void *opaque, int event) + + } + ++static const QemuChrHandlers serial_handlers = { ++ .fd_can_read = serial_can_receive, ++ .fd_read = serial_receive, ++ .fd_event = serial_event, ++}; ++ + static int etraxfs_ser_init(SysBusDevice *dev) + { + struct etrax_serial *s = FROM_SYSBUS(typeof (*s), dev); +@@ -204,10 +210,9 @@ static int etraxfs_ser_init(SysBusDevice *dev) + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, R_MAX * 4, ser_regs); + s->chr = qdev_init_chardev(&dev->qdev); +- if (s->chr) +- qemu_chr_add_handlers(s->chr, +- serial_can_receive, serial_receive, +- serial_event, s); ++ if (s->chr) { ++ qemu_chr_add_handlers(s->chr, &serial_handlers, s); ++ } + return 0; + } + +diff --git a/hw/grlib_apbuart.c b/hw/grlib_apbuart.c +index 101b150..40d6968 100644 +--- a/hw/grlib_apbuart.c ++++ b/hw/grlib_apbuart.c +@@ -144,16 +144,18 @@ static CPUWriteMemoryFunc * const grlib_apbuart_write[] = { + NULL, NULL, grlib_apbuart_writel, + }; + ++static const QemuChrHandlers grlib_handlers = { ++ .fd_can_read = grlib_apbuart_can_receive, ++ .fd_read = grlib_apbuart_receive, ++ .fd_event = grlib_apbuart_event, ++}; ++ + static int grlib_apbuart_init(SysBusDevice *dev) + { + UART *uart = FROM_SYSBUS(typeof(*uart), dev); + int uart_regs = 0; + +- qemu_chr_add_handlers(uart->chr, +- grlib_apbuart_can_receive, +- grlib_apbuart_receive, +- grlib_apbuart_event, +- uart); ++ qemu_chr_add_handlers(uart->chr, &grlib_handlers, uart); + + sysbus_init_irq(dev, &uart->irq); + +diff --git a/hw/ivshmem.c b/hw/ivshmem.c +index 7b19a81..ef8e5ce 100644 +--- a/hw/ivshmem.c ++++ b/hw/ivshmem.c +@@ -312,6 +312,18 @@ static void fake_irqfd(void *opaque, const uint8_t *buf, int size) { + msix_notify(pdev, entry->vector); + } + ++static const QemuChrHandlers ivshmem_handlers = { ++ .fd_can_read = ivshmem_can_receive, ++ .fd_read = ivshmem_receive, ++ .fd_event = ivshmem_event, ++}; ++ ++static const QemuChrHandlers ivshmem_msi_handlers = { ++ .fd_can_read = ivshmem_can_receive, ++ .fd_read = fake_irqfd, ++ .fd_event = ivshmem_event, ++}; ++ + static CharDriverState* create_eventfd_chr_device(void * opaque, int eventfd, + int vector) + { +@@ -331,11 +343,10 @@ static CharDriverState* create_eventfd_chr_device(void * opaque, int eventfd, + s->eventfd_table[vector].pdev = &s->dev; + s->eventfd_table[vector].vector = vector; + +- qemu_chr_add_handlers(chr, ivshmem_can_receive, fake_irqfd, +- ivshmem_event, &s->eventfd_table[vector]); ++ qemu_chr_add_handlers(chr, &ivshmem_msi_handlers, ++ &s->eventfd_table[vector]); + } else { +- qemu_chr_add_handlers(chr, ivshmem_can_receive, ivshmem_receive, +- ivshmem_event, s); ++ qemu_chr_add_handlers(chr, &ivshmem_handlers, s); + } + + return chr; +@@ -666,6 +677,12 @@ static int ivshmem_load(QEMUFile* f, void *opaque, int version_id) + return 0; + } + ++static const QemuChrHandlers ivshmem_server_handlers = { ++ .fd_can_read = ivshmem_can_receive, ++ .fd_read = ivshmem_read, ++ .fd_event = ivshmem_event, ++}; ++ + static int pci_ivshmem_init(PCIDevice *dev) + { + IVShmemState *s = DO_UPCAST(IVShmemState, dev, dev); +@@ -754,8 +771,7 @@ static int pci_ivshmem_init(PCIDevice *dev) + + s->eventfd_chr = qemu_mallocz(s->vectors * sizeof(CharDriverState *)); + +- qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive, ivshmem_read, +- ivshmem_event, s); ++ qemu_chr_add_handlers(s->server_chr, &ivshmem_server_handlers, s); + } else { + /* just map the file immediately, we're not using a server */ + int fd; +diff --git a/hw/mcf_uart.c b/hw/mcf_uart.c +index db57096..9928c11 100644 +--- a/hw/mcf_uart.c ++++ b/hw/mcf_uart.c +@@ -268,6 +268,12 @@ static void mcf_uart_receive(void *opaque, const uint8_t *buf, int size) + mcf_uart_push_byte(s, buf[0]); + } + ++static const QemuChrHandlers mcf_uart_handlers = { ++ .fd_can_read = mcf_uart_can_receive, ++ .fd_read = mcf_uart_receive, ++ .fd_event = mcf_uart_event, ++}; ++ + void *mcf_uart_init(qemu_irq irq, CharDriverState *chr) + { + mcf_uart_state *s; +@@ -276,8 +282,7 @@ void *mcf_uart_init(qemu_irq irq, CharDriverState *chr) + s->chr = chr; + s->irq = irq; + if (chr) { +- qemu_chr_add_handlers(chr, mcf_uart_can_receive, mcf_uart_receive, +- mcf_uart_event, s); ++ qemu_chr_add_handlers(chr, &mcf_uart_handlers, s); + } + mcf_uart_reset(s); + return s; +diff --git a/hw/pl011.c b/hw/pl011.c +index 77f0dbf..d93c655 100644 +--- a/hw/pl011.c ++++ b/hw/pl011.c +@@ -286,6 +286,12 @@ static int pl011_load(QEMUFile *f, void *opaque, int version_id) + return 0; + } + ++static const QemuChrHandlers pl011_handlers = { ++ .fd_can_read = pl011_can_receive, ++ .fd_read = pl011_receive, ++ .fd_event = pl011_event, ++}; ++ + static int pl011_init(SysBusDevice *dev, const unsigned char *id) + { + int iomemtype; +@@ -304,8 +310,7 @@ static int pl011_init(SysBusDevice *dev, const unsigned char *id) + s->cr = 0x300; + s->flags = 0x90; + if (s->chr) { +- qemu_chr_add_handlers(s->chr, pl011_can_receive, pl011_receive, +- pl011_event, s); ++ qemu_chr_add_handlers(s->chr, &pl011_handlers, s); + } + register_savevm(&dev->qdev, "pl011_uart", -1, 1, pl011_save, pl011_load, s); + return 0; +diff --git a/hw/pxa2xx.c b/hw/pxa2xx.c +index d966846..d7ebf33 100644 +--- a/hw/pxa2xx.c ++++ b/hw/pxa2xx.c +@@ -1995,6 +1995,12 @@ static int pxa2xx_fir_load(QEMUFile *f, void *opaque, int version_id) + return 0; + } + ++static const QemuChrHandlers pxa2xx_handlers = { ++ .fd_can_read = pxa2xx_fir_is_empty, ++ .fd_read = pxa2xx_fir_rx, ++ .fd_event = pxa2xx_fir_event, ++}; ++ + static PXA2xxFIrState *pxa2xx_fir_init(target_phys_addr_t base, + qemu_irq irq, PXA2xxDMAState *dma, + CharDriverState *chr) +@@ -2013,10 +2019,9 @@ static PXA2xxFIrState *pxa2xx_fir_init(target_phys_addr_t base, + pxa2xx_fir_writefn, s, DEVICE_NATIVE_ENDIAN); + cpu_register_physical_memory(base, 0x1000, iomemtype); + +- if (chr) +- qemu_chr_add_handlers(chr, pxa2xx_fir_is_empty, +- pxa2xx_fir_rx, pxa2xx_fir_event, s); +- ++ if (chr) { ++ qemu_chr_add_handlers(chr, &pxa2xx_handlers, s); ++ } + register_savevm(NULL, "pxa2xx_fir", 0, 0, pxa2xx_fir_save, + pxa2xx_fir_load, s); + +diff --git a/hw/serial.c b/hw/serial.c +index 2c4af61..65265e2 100644 +--- a/hw/serial.c ++++ b/hw/serial.c +@@ -727,6 +727,12 @@ static void serial_reset(void *opaque) + qemu_irq_lower(s->irq); + } + ++static const QemuChrHandlers serial_handlers = { ++ .fd_can_read = serial_can_receive1, ++ .fd_read = serial_receive1, ++ .fd_event = serial_event, ++}; ++ + static void serial_init_core(SerialState *s) + { + if (!s->chr) { +@@ -741,8 +747,7 @@ static void serial_init_core(SerialState *s) + + qemu_register_reset(serial_reset, s); + +- qemu_chr_add_handlers(s->chr, serial_can_receive1, serial_receive1, +- serial_event, s); ++ qemu_chr_add_handlers(s->chr, &serial_handlers, s); + } + + /* Change the main reference oscillator frequency. */ +diff --git a/hw/sh_serial.c b/hw/sh_serial.c +index 191f4a6..8b6460d 100644 +--- a/hw/sh_serial.c ++++ b/hw/sh_serial.c +@@ -350,6 +350,12 @@ static CPUWriteMemoryFunc * const sh_serial_writefn[] = { + &sh_serial_write, + }; + ++static const QemuChrHandlers sh_serial_handlers = { ++ .fd_can_read = sh_serial_can_receive1, ++ .fd_read = sh_serial_receive1, ++ .fd_event = sh_serial_event, ++}; ++ + void sh_serial_init (target_phys_addr_t base, int feat, + uint32_t freq, CharDriverState *chr, + qemu_irq eri_source, +@@ -389,9 +395,9 @@ void sh_serial_init (target_phys_addr_t base, int feat, + + s->chr = chr; + +- if (chr) +- qemu_chr_add_handlers(chr, sh_serial_can_receive1, sh_serial_receive1, +- sh_serial_event, s); ++ if (chr) { ++ qemu_chr_add_handlers(chr, &sh_serial_handlers, s); ++ } + + s->eri = eri_source; + s->rxi = rxi_source; +diff --git a/hw/syborg_serial.c b/hw/syborg_serial.c +index 34ce076..124b636 100644 +--- a/hw/syborg_serial.c ++++ b/hw/syborg_serial.c +@@ -315,6 +315,12 @@ static int syborg_serial_load(QEMUFile *f, void *opaque, int version_id) + return 0; + } + ++static const QemuChrHandlers syborg_serial_handlers = { ++ .fd_can_read = syborg_serial_can_receive, ++ .fd_read = syborg_serial_receive, ++ .fd_event = syborg_serial_event, ++}; ++ + static int syborg_serial_init(SysBusDevice *dev) + { + SyborgSerialState *s = FROM_SYSBUS(SyborgSerialState, dev); +@@ -327,8 +333,7 @@ static int syborg_serial_init(SysBusDevice *dev) + sysbus_init_mmio(dev, 0x1000, iomemtype); + s->chr = qdev_init_chardev(&dev->qdev); + if (s->chr) { +- qemu_chr_add_handlers(s->chr, syborg_serial_can_receive, +- syborg_serial_receive, syborg_serial_event, s); ++ qemu_chr_add_handlers(s->chr, &syborg_serial_handlers, s); + } + if (s->fifo_size <= 0) { + fprintf(stderr, "syborg_serial: fifo too small\n"); +diff --git a/hw/usb-serial.c b/hw/usb-serial.c +index 6763d52..2435d9d 100644 +--- a/hw/usb-serial.c ++++ b/hw/usb-serial.c +@@ -475,6 +475,12 @@ static void usb_serial_event(void *opaque, int event) + } + } + ++static const QemuChrHandlers usb_serial_handlers = { ++ .fd_can_read = usb_serial_can_read, ++ .fd_read = usb_serial_read, ++ .fd_event = usb_serial_event, ++}; ++ + static int usb_serial_initfn(USBDevice *dev) + { + USBSerialState *s = DO_UPCAST(USBSerialState, dev, dev); +@@ -486,8 +492,7 @@ static int usb_serial_initfn(USBDevice *dev) + return -1; + } + +- qemu_chr_add_handlers(s->cs, usb_serial_can_read, usb_serial_read, +- usb_serial_event, s); ++ qemu_chr_add_handlers(s->cs, &usb_serial_handlers, s); + usb_serial_handle_reset(dev); + return 0; + } +diff --git a/hw/virtio-console.c b/hw/virtio-console.c +index 62624ec..22cf28c 100644 +--- a/hw/virtio-console.c ++++ b/hw/virtio-console.c +@@ -57,13 +57,18 @@ static void chr_event(void *opaque, int event) + } + } + ++static const QemuChrHandlers chr_handlers = { ++ .fd_can_read = chr_can_read, ++ .fd_read = chr_read, ++ .fd_event = chr_event, ++}; ++ + static int generic_port_init(VirtConsole *vcon, VirtIOSerialDevice *dev) + { + vcon->port.info = dev->info; + + if (vcon->chr) { +- qemu_chr_add_handlers(vcon->chr, chr_can_read, chr_read, chr_event, +- vcon); ++ qemu_chr_add_handlers(vcon->chr, &chr_handlers, vcon); + vcon->port.info->have_data = flush_buf; + } + return 0; +diff --git a/hw/xen_console.c b/hw/xen_console.c +index d2261f4..8327e4e 100644 +--- a/hw/xen_console.c ++++ b/hw/xen_console.c +@@ -202,6 +202,11 @@ static int con_init(struct XenDevice *xendev) + return 0; + } + ++static const QemuChrHandlers xencons_handlers = { ++ .fd_can_read = xencons_can_receive, ++ .fd_read = xencons_receive, ++}; ++ + static int con_connect(struct XenDevice *xendev) + { + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); +@@ -222,9 +227,9 @@ static int con_connect(struct XenDevice *xendev) + return -1; + + xen_be_bind_evtchn(&con->xendev); +- if (con->chr) +- qemu_chr_add_handlers(con->chr, xencons_can_receive, xencons_receive, +- NULL, con); ++ if (con->chr) { ++ qemu_chr_add_handlers(con->chr, &xencons_handlers, con); ++ } + + xen_be_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n", + con->ring_ref, +@@ -238,8 +243,9 @@ static void con_disconnect(struct XenDevice *xendev) + { + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + +- if (con->chr) +- qemu_chr_add_handlers(con->chr, NULL, NULL, NULL, NULL); ++ if (con->chr) { ++ qemu_chr_add_handlers(con->chr, NULL, NULL); ++ } + xen_be_unbind_evtchn(&con->xendev); + + if (con->sring) { +diff --git a/hw/xilinx_uartlite.c b/hw/xilinx_uartlite.c +index 9b94e98..1845577 100644 +--- a/hw/xilinx_uartlite.c ++++ b/hw/xilinx_uartlite.c +@@ -193,6 +193,12 @@ static void uart_event(void *opaque, int event) + + } + ++static const QemuChrHandlers uart_handlers = { ++ .fd_can_read = uart_can_rx, ++ .fd_read = uart_rx, ++ .fd_event = uart_event, ++}; ++ + static int xilinx_uartlite_init(SysBusDevice *dev) + { + struct xlx_uartlite *s = FROM_SYSBUS(typeof (*s), dev); +@@ -206,8 +212,9 @@ static int xilinx_uartlite_init(SysBusDevice *dev) + sysbus_init_mmio(dev, R_MAX * 4, uart_regs); + + s->chr = qdev_init_chardev(&dev->qdev); +- if (s->chr) +- qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); ++ if (s->chr) { ++ qemu_chr_add_handlers(s->chr, &uart_handlers, s); ++ } + return 0; + } + +diff --git a/monitor.c b/monitor.c +index 096d42b..a00a233 100644 +--- a/monitor.c ++++ b/monitor.c +@@ -5179,6 +5179,18 @@ static void monitor_event(void *opaque, int event) + * End: + */ + ++static const QemuChrHandlers monitor_handlers = { ++ .fd_can_read = monitor_can_read, ++ .fd_read = monitor_read, ++ .fd_event = monitor_event, ++}; ++ ++static const QemuChrHandlers monitor_control_handlers = { ++ .fd_can_read = monitor_can_read, ++ .fd_read = monitor_control_read, ++ .fd_event = monitor_control_event, ++}; ++ + void monitor_init(CharDriverState *chr, int flags) + { + static int is_first_init = 1; +@@ -5201,12 +5213,10 @@ void monitor_init(CharDriverState *chr, int flags) + if (monitor_ctrl_mode(mon)) { + mon->mc = qemu_mallocz(sizeof(MonitorControl)); + /* Control mode requires special handlers */ +- qemu_chr_add_handlers(chr, monitor_can_read, monitor_control_read, +- monitor_control_event, mon); ++ qemu_chr_add_handlers(chr, &monitor_control_handlers, mon); + qemu_chr_set_echo(chr, true); + } else { +- qemu_chr_add_handlers(chr, monitor_can_read, monitor_read, +- monitor_event, mon); ++ qemu_chr_add_handlers(chr, &monitor_handlers, mon); + } + + QLIST_INSERT_HEAD(&mon_list, mon, entry); +diff --git a/net/slirp.c b/net/slirp.c +index b41c60a..437be46 100644 +--- a/net/slirp.c ++++ b/net/slirp.c +@@ -577,6 +577,11 @@ static void guestfwd_read(void *opaque, const uint8_t *buf, int size) + slirp_socket_recv(fwd->slirp, fwd->server, fwd->port, buf, size); + } + ++static const QemuChrHandlers guestfwd_handlers = { ++ .fd_can_read = guestfwd_can_read, ++ .fd_read = guestfwd_read, ++}; ++ + static int slirp_guestfwd(SlirpState *s, const char *config_str, + int legacy_format) + { +@@ -633,8 +638,7 @@ static int slirp_guestfwd(SlirpState *s, const char *config_str, + fwd->port = port; + fwd->slirp = s->slirp; + +- qemu_chr_add_handlers(fwd->hd, guestfwd_can_read, guestfwd_read, +- NULL, fwd); ++ qemu_chr_add_handlers(fwd->hd, &guestfwd_handlers, fwd); + return 0; + + fail_syntax: +diff --git a/qemu-char.c b/qemu-char.c +index 4b57af9..3a31d8b 100644 +--- a/qemu-char.c ++++ b/qemu-char.c +@@ -191,15 +191,22 @@ void qemu_chr_send_event(CharDriverState *s, int event) + s->chr_send_event(s, event); + } + ++static const QemuChrHandlers null_handlers = { ++ /* All handlers are initialised to NULL */ ++}; ++ + void qemu_chr_add_handlers(CharDriverState *s, +- IOCanReadHandler *fd_can_read, +- IOReadHandler *fd_read, +- IOEventHandler *fd_event, +- void *opaque) +-{ +- s->chr_can_read = fd_can_read; +- s->chr_read = fd_read; +- s->chr_event = fd_event; ++ const QemuChrHandlers *handlers, void *opaque) ++{ ++ if (!s) { ++ return; ++ } ++ if (!handlers) { ++ handlers = &null_handlers; ++ } ++ s->chr_can_read = handlers->fd_can_read; ++ s->chr_read = handlers->fd_read; ++ s->chr_event = handlers->fd_event; + s->handler_opaque = opaque; + if (s->chr_update_read_handler) + s->chr_update_read_handler(s); +@@ -437,6 +444,12 @@ static void mux_chr_event(void *opaque, int event) + mux_chr_send_event(d, i, event); + } + ++static const QemuChrHandlers mux_chr_handlers = { ++ .fd_can_read = mux_chr_can_read, ++ .fd_read = mux_chr_read, ++ .fd_event = mux_chr_event, ++}; ++ + static void mux_chr_update_read_handler(CharDriverState *chr) + { + MuxDriver *d = chr->opaque; +@@ -451,8 +464,7 @@ static void mux_chr_update_read_handler(CharDriverState *chr) + d->chr_event[d->mux_cnt] = chr->chr_event; + /* Fix up the real driver with mux routines */ + if (d->mux_cnt == 0) { +- qemu_chr_add_handlers(d->drv, mux_chr_can_read, mux_chr_read, +- mux_chr_event, chr); ++ qemu_chr_add_handlers(d->drv, &mux_chr_handlers, chr); + } + if (d->focus != -1) { + mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT); +diff --git a/qemu-char.h b/qemu-char.h +index 56d9954..7a1924c 100644 +--- a/qemu-char.h ++++ b/qemu-char.h +@@ -1,6 +1,7 @@ + #ifndef QEMU_CHAR_H + #define QEMU_CHAR_H + ++#include + #include "qemu-common.h" + #include "qemu-queue.h" + #include "qemu-option.h" +@@ -73,6 +74,13 @@ struct CharDriverState { + QTAILQ_ENTRY(CharDriverState) next; + }; + ++typedef struct QemuChrHandlers { ++ IOCanReadHandler *fd_can_read; ++ IOReadHandler *fd_read; ++ IOHandler *fd_write_unblocked; ++ IOEventHandler *fd_event; ++} QemuChrHandlers; ++ + QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename); + CharDriverState *qemu_chr_open_opts(QemuOpts *opts, + void (*init)(struct CharDriverState *s)); +@@ -83,10 +91,7 @@ void qemu_chr_printf(CharDriverState *s, const char *fmt, ...) + GCC_FMT_ATTR(2, 3); + int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len); + void qemu_chr_send_event(CharDriverState *s, int event); +-void qemu_chr_add_handlers(CharDriverState *s, +- IOCanReadHandler *fd_can_read, +- IOReadHandler *fd_read, +- IOEventHandler *fd_event, ++void qemu_chr_add_handlers(CharDriverState *s, const QemuChrHandlers *handlers, + void *opaque); + int qemu_chr_ioctl(CharDriverState *s, int cmd, void *arg); + void qemu_chr_generic_open(CharDriverState *s); +-- +1.7.4.1 + diff --git a/0007-iohandlers-Add-enable-disable_write_fd_handler-funct.patch b/0007-iohandlers-Add-enable-disable_write_fd_handler-funct.patch new file mode 100644 index 0000000..336c582 --- /dev/null +++ b/0007-iohandlers-Add-enable-disable_write_fd_handler-funct.patch @@ -0,0 +1,76 @@ +>From da7e6cd863ed0cffe885cd2d3639f92c82baf6e2 Mon Sep 17 00:00:00 2001 +From: Amit Shah +Date: Mon, 21 Mar 2011 20:32:58 +0100 +Subject: [PATCH 07/17] iohandlers: Add enable/disable_write_fd_handler() functions + +These will be used to provide a cleaner API for the nonblocking case. + +Signed-off-by: Amit Shah +--- + qemu-char.h | 3 +++ + vl.c | 35 +++++++++++++++++++++++++++++++++++ + 2 files changed, 38 insertions(+), 0 deletions(-) + +diff --git a/qemu-char.h b/qemu-char.h +index 7a1924c..185377c 100644 +--- a/qemu-char.h ++++ b/qemu-char.h +@@ -116,6 +116,9 @@ size_t qemu_chr_mem_osize(const CharDriverState *chr); + + /* async I/O support */ + ++void enable_write_fd_handler(int fd, IOHandler *fd_write); ++void disable_write_fd_handler(int fd); ++ + int qemu_set_fd_handler2(int fd, + IOCanReadHandler *fd_read_poll, + IOHandler *fd_read, +diff --git a/vl.c b/vl.c +index 85c36e3..95f51cb 100644 +--- a/vl.c ++++ b/vl.c +@@ -1044,6 +1044,41 @@ typedef struct IOHandlerRecord { + static QLIST_HEAD(, IOHandlerRecord) io_handlers = + QLIST_HEAD_INITIALIZER(io_handlers); + ++static IOHandlerRecord *find_iohandler(int fd) ++{ ++ IOHandlerRecord *ioh; ++ ++ QLIST_FOREACH(ioh, &io_handlers, next) { ++ if (ioh->fd == fd) { ++ return ioh; ++ } ++ } ++ return NULL; ++} ++ ++void enable_write_fd_handler(int fd, IOHandler *fd_write) ++{ ++ IOHandlerRecord *ioh; ++ ++ ioh = find_iohandler(fd); ++ if (!ioh) { ++ return; ++ } ++ ++ ioh->fd_write = fd_write; ++} ++ ++void disable_write_fd_handler(int fd) ++{ ++ IOHandlerRecord *ioh; ++ ++ ioh = find_iohandler(fd); ++ if (!ioh) { ++ return; ++ } ++ ++ ioh->fd_write = NULL; ++} + + /* XXX: fd_read_poll should be suppressed, but an API change is + necessary in the character devices to suppress fd_can_read(). */ +-- +1.7.3.2 + diff --git a/0008-char-Add-framework-for-a-write-unblocked-callback.patch b/0008-char-Add-framework-for-a-write-unblocked-callback.patch new file mode 100644 index 0000000..ab3eb1f --- /dev/null +++ b/0008-char-Add-framework-for-a-write-unblocked-callback.patch @@ -0,0 +1,62 @@ +>From daf37480ffe37b3e7a781ff010beb4fa89821c29 Mon Sep 17 00:00:00 2001 +From: Amit Shah +Date: Mon, 21 Mar 2011 21:41:42 +0100 +Subject: [PATCH 08/17] char: Add framework for a 'write unblocked' callback + +The char layer can let users know that the driver will block on further +input. For users interested in not blocking, they can assign a function +pointer that will be called back when the driver becomes writable. This +patch just adds the function pointers to the CharDriverState structure, +future patches will enable the nonblocking and callback functionality. + +Signed-off-by: Amit Shah +--- + qemu-char.c | 3 +++ + qemu-char.h | 5 +++++ + 2 files changed, 8 insertions(+), 0 deletions(-) + +diff --git a/qemu-char.c b/qemu-char.c +index 3a31d8b..ce76411 100644 +--- a/qemu-char.c ++++ b/qemu-char.c +@@ -206,11 +206,14 @@ void qemu_chr_add_handlers(CharDriverState *s, + } + s->chr_can_read = handlers->fd_can_read; + s->chr_read = handlers->fd_read; ++ s->chr_write_unblocked = handlers->fd_write_unblocked; + s->chr_event = handlers->fd_event; + s->handler_opaque = opaque; + if (s->chr_update_read_handler) + s->chr_update_read_handler(s); + ++ s->write_blocked = false; ++ + /* We're connecting to an already opened device, so let's make sure we + also get the open event */ + if (s->opened) { +diff --git a/qemu-char.h b/qemu-char.h +index 185377c..bf06da0 100644 +--- a/qemu-char.h ++++ b/qemu-char.h +@@ -61,6 +61,9 @@ struct CharDriverState { + IOEventHandler *chr_event; + IOCanReadHandler *chr_can_read; + IOReadHandler *chr_read; ++ IOHandler *chr_write_unblocked; ++ void (*chr_enable_write_fd_handler)(struct CharDriverState *chr); ++ void (*chr_disable_write_fd_handler)(struct CharDriverState *chr); + void *handler_opaque; + void (*chr_send_event)(struct CharDriverState *chr, int event); + void (*chr_close)(struct CharDriverState *chr); +@@ -71,6 +74,8 @@ struct CharDriverState { + char *label; + char *filename; + int opened; ++ /* Are we in a blocked state? */ ++ bool write_blocked; + QTAILQ_ENTRY(CharDriverState) next; + }; + +-- +1.7.3.2 + diff --git a/0009-char-Update-send_all-to-handle-nonblocking-chardev-w.patch b/0009-char-Update-send_all-to-handle-nonblocking-chardev-w.patch new file mode 100644 index 0000000..ccf98ad --- /dev/null +++ b/0009-char-Update-send_all-to-handle-nonblocking-chardev-w.patch @@ -0,0 +1,197 @@ +>From 8b73193a8584da4e93bccd93fe6f0b8f1a1612b3 Mon Sep 17 00:00:00 2001 +From: Amit Shah +Date: Mon, 21 Mar 2011 22:00:27 +0100 +Subject: [PATCH 09/17] char: Update send_all() to handle nonblocking chardev write requests + +The send_all function is modified to return to the caller in case the +driver cannot handle any more data. It returns -EAGAIN or +WSAEWOULDBLOCK on non-Windows and Windows platforms respectively. This +is only done when the caller sets a callback function handler indicating +it's not interested in blocking till the driver has written out all the +data. + +Currently there's no driver or caller that supports this. Future +commits will add such capability. + +Signed-off-by: Amit Shah +--- + net/socket.c | 4 +- + qemu-char.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- + qemu_socket.h | 2 +- + 3 files changed, 76 insertions(+), 9 deletions(-) + +diff --git a/net/socket.c b/net/socket.c +index 3182b37..5dedd78 100644 +--- a/net/socket.c ++++ b/net/socket.c +@@ -56,8 +56,8 @@ static ssize_t net_socket_receive(VLANClientState *nc, const uint8_t *buf, size_ + uint32_t len; + len = htonl(size); + +- send_all(s->fd, (const uint8_t *)&len, sizeof(len)); +- return send_all(s->fd, buf, size); ++ send_all(NULL, s->fd, (const uint8_t *)&len, sizeof(len)); ++ return send_all(NULL, s->fd, buf, size); + } + + static ssize_t net_socket_receive_dgram(VLANClientState *nc, const uint8_t *buf, size_t size) +diff --git a/qemu-char.c b/qemu-char.c +index ce76411..eed61d6 100644 +--- a/qemu-char.c ++++ b/qemu-char.c +@@ -500,7 +500,7 @@ static CharDriverState *qemu_chr_open_mux(CharDriverState *drv) + + + #ifdef _WIN32 +-int send_all(int fd, const void *buf, int len1) ++static int do_send(int fd, const void *buf, int len1, bool nonblock) + { + int ret, len; + +@@ -508,9 +508,14 @@ int send_all(int fd, const void *buf, int len1) + while (len > 0) { + ret = send(fd, buf, len, 0); + if (ret < 0) { ++ if (nonblock && len1 - len) { ++ return len1 - len; ++ } + errno = WSAGetLastError(); + if (errno != WSAEWOULDBLOCK) { + return -1; ++ } else if (errno == WSAEWOULDBLOCK && nonblock) { ++ return WSAEWOULDBLOCK; + } + } else if (ret == 0) { + break; +@@ -524,7 +529,7 @@ int send_all(int fd, const void *buf, int len1) + + #else + +-int send_all(int fd, const void *_buf, int len1) ++static int do_send(int fd, const void *_buf, int len1, bool nonblock) + { + int ret, len; + const uint8_t *buf = _buf; +@@ -533,8 +538,15 @@ int send_all(int fd, const void *_buf, int len1) + while (len > 0) { + ret = write(fd, buf, len); + if (ret < 0) { +- if (errno != EINTR && errno != EAGAIN) ++ if (nonblock && len1 - len) { ++ return len1 - len; ++ } ++ if (errno == EAGAIN && nonblock) { ++ return -EAGAIN; ++ } ++ if (errno != EINTR && errno != EAGAIN) { + return -1; ++ } + } else if (ret == 0) { + break; + } else { +@@ -546,6 +558,55 @@ int send_all(int fd, const void *_buf, int len1) + } + #endif /* !_WIN32 */ + ++int send_all(CharDriverState *chr, int fd, const void *_buf, int len1) ++{ ++ int ret, eagain_errno; ++ bool nonblock; ++ ++ if (chr && chr->write_blocked) { ++ /* ++ * We don't handle this situation: the caller should not send ++ * us data while we're blocked. ++ * ++ * We could buffer this data here but that'll only encourage ++ * bad behaviour on part of the callers. ++ * ++ * Also, the data already in fd's buffers isn't easily ++ * migratable. If we want full migration support, all the ++ * data landing here needs to be buffered and on migration, ++ * anything that's unsent needs to be transferred to the ++ * dest. machine (which again isn't a very good way of solving ++ * the problem, as the src may become writable just during ++ * migration and the reader could receive some data twice, ++ * essentially corrupting the data). ++ */ ++ abort(); ++ } ++ ++ nonblock = false; ++ /* ++ * Ensure the char backend is able to receive and handle the ++ * 'write unblocked' event before we turn on nonblock support. ++ */ ++ if (chr && chr->chr_enable_write_fd_handler && chr->chr_write_unblocked) { ++ nonblock = true; ++ } ++ ret = do_send(fd, _buf, len1, nonblock); ++ ++#ifdef _WIN32 ++ eagain_errno = WSAEWOULDBLOCK; ++#else ++ eagain_errno = -EAGAIN; ++#endif ++ ++ if (nonblock && (ret == eagain_errno || (ret >= 0 && ret < len1))) { ++ /* Update fd handler to wake up when chr becomes writable */ ++ chr->chr_enable_write_fd_handler(chr); ++ chr->write_blocked = true; ++ } ++ return ret; ++} ++ + #ifndef _WIN32 + + typedef struct { +@@ -559,7 +620,7 @@ static int stdio_nb_clients = 0; + static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len) + { + FDCharDriver *s = chr->opaque; +- return send_all(s->fd_out, buf, len); ++ return send_all(chr, s->fd_out, buf, len); + } + + static int fd_chr_read_poll(void *opaque) +@@ -875,7 +936,7 @@ static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len) + pty_chr_update_read_handler(chr); + return 0; + } +- return send_all(s->fd, buf, len); ++ return send_all(chr, s->fd, buf, len); + } + + static int pty_chr_read_poll(void *opaque) +@@ -1944,8 +2005,14 @@ static void tcp_closed(void *opaque) + static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) + { + TCPCharDriver *s = chr->opaque; ++ + if (s->connected) { +- return send_all(s->fd, buf, len); ++ int ret; ++ ++ ret = send_all(chr, s->fd, buf, len); ++ if (ret == -1 && errno == EPIPE) { ++ tcp_closed(chr); ++ } + } else { + /* XXX: indicate an error ? */ + return len; +diff --git a/qemu_socket.h b/qemu_socket.h +index 897a8ae..97dd24a 100644 +--- a/qemu_socket.h ++++ b/qemu_socket.h +@@ -36,7 +36,7 @@ int inet_aton(const char *cp, struct in_addr *ia); + int qemu_socket(int domain, int type, int protocol); + int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen); + void socket_set_nonblock(int fd); +-int send_all(int fd, const void *buf, int len1); ++int send_all(CharDriverState *chr, int fd, const void *buf, int len1); + + /* New, ipv6-ready socket helper functions, see qemu-sockets.c */ + int inet_listen_opts(QemuOpts *opts, int port_offset); +-- +1.7.3.2 + diff --git a/0010-char-Equip-the-unix-tcp-backend-to-handle-nonblockin.patch b/0010-char-Equip-the-unix-tcp-backend-to-handle-nonblockin.patch new file mode 100644 index 0000000..f08d700 --- /dev/null +++ b/0010-char-Equip-the-unix-tcp-backend-to-handle-nonblockin.patch @@ -0,0 +1,80 @@ +>From 7d8cbead9454da6dbfdc050c6828faae39621a1b Mon Sep 17 00:00:00 2001 +From: Amit Shah +Date: Mon, 21 Mar 2011 22:02:47 +0100 +Subject: [PATCH 10/17] char: Equip the unix/tcp backend to handle nonblocking writes# + +Now that the infrastructure is in place to return -EAGAIN to callers, +individual char drivers can set their update_fd_handlers() function to +set or remove an fd's write handler. This handler checks if the driver +became writable. + +A generic callback routine is used for unblocking writes and letting +users of chardevs know that a driver became writable again. + +Signed-off-by: Amit Shah +--- + qemu-char.c | 34 ++++++++++++++++++++++++++++++++++ + 1 files changed, 34 insertions(+), 0 deletions(-) + +diff --git a/qemu-char.c b/qemu-char.c +index eed61d6..7517f64 100644 +--- a/qemu-char.c ++++ b/qemu-char.c +@@ -107,6 +107,19 @@ + static QTAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs = + QTAILQ_HEAD_INITIALIZER(chardevs); + ++/* ++ * Generic routine that gets called when chardev becomes writable. ++ * Lets chardev user know it's OK to send more data. ++ */ ++static void char_write_unblocked(void *opaque) ++{ ++ CharDriverState *chr = opaque; ++ ++ chr->write_blocked = false; ++ chr->chr_disable_write_fd_handler(chr); ++ chr->chr_write_unblocked(chr->handler_opaque); ++} ++ + static void qemu_chr_event(CharDriverState *s, int event) + { + /* Keep track if the char device is open */ +@@ -2261,6 +2274,25 @@ static void tcp_chr_close(CharDriverState *chr) + qemu_chr_event(chr, CHR_EVENT_CLOSED); + } + ++static void tcp_enable_write_fd_handler(CharDriverState *chr) ++{ ++ TCPCharDriver *s = chr->opaque; ++ ++ /* ++ * This function is called only after tcp_chr_connect() is called ++ * (either in 'server' mode or client mode. So we're sure of ++ * s->fd being initialised. ++ */ ++ enable_write_fd_handler(s->fd, char_write_unblocked); ++} ++ ++static void tcp_disable_write_fd_handler(CharDriverState *chr) ++{ ++ TCPCharDriver *s = chr->opaque; ++ ++ disable_write_fd_handler(s->fd); ++} ++ + static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) + { + CharDriverState *chr = NULL; +@@ -2313,6 +2345,8 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) + chr->chr_write = tcp_chr_write; + chr->chr_close = tcp_chr_close; + chr->get_msgfd = tcp_get_msgfd; ++ chr->chr_enable_write_fd_handler = tcp_enable_write_fd_handler; ++ chr->chr_disable_write_fd_handler = tcp_disable_write_fd_handler; + + if (is_listen) { + s->listen_fd = fd; +-- +1.7.3.2 + diff --git a/0011-char-Throttle-when-host-connection-is-down.patch b/0011-char-Throttle-when-host-connection-is-down.patch new file mode 100644 index 0000000..78f906a --- /dev/null +++ b/0011-char-Throttle-when-host-connection-is-down.patch @@ -0,0 +1,56 @@ +>From 473be206466567646e3377b8eb64e25ffc2b3afe Mon Sep 17 00:00:00 2001 +From: Amit Shah +Date: Mon, 21 Mar 2011 22:05:10 +0100 +Subject: [PATCH 11/17] char: Throttle when host connection is down# + +When the host-side connection goes down, throttle the virtio-serial bus +and later unthrottle when a connection gets established. This helps +prevent any lost IO (guest->host) while the host connection was down. + +Bugzilla: 621484 + +This commit actually helps the bug mentioned above as no writes will now +get lost because of the throttling done here. With just the patches +sent earlier for that bug, one write will end up getting lost in the +worst case (host d/c, guest write, host connect). + +Signed-off-by: Amit Shah +--- + qemu-char.c | 14 ++++++++++++++ + 1 files changed, 14 insertions(+), 0 deletions(-) + +diff --git a/qemu-char.c b/qemu-char.c +index 7517f64..2ef972f 100644 +--- a/qemu-char.c ++++ b/qemu-char.c +@@ -141,6 +141,9 @@ static void qemu_chr_generic_open_bh(void *opaque) + { + CharDriverState *s = opaque; + qemu_chr_event(s, CHR_EVENT_OPENED); ++ if (s->write_blocked) { ++ char_write_unblocked(s); ++ } + qemu_bh_delete(s->bh); + s->bh = NULL; + } +@@ -2025,6 +2028,17 @@ static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) + ret = send_all(chr, s->fd, buf, len); + if (ret == -1 && errno == EPIPE) { + tcp_closed(chr); ++ ++ if (chr->chr_enable_write_fd_handler && chr->chr_write_unblocked) { ++ /* ++ * Since we haven't written out anything, let's say ++ * we're throttled. This will prevent any output from ++ * the guest getting lost if host-side chardev goes ++ * down. Unthrottle when we re-connect. ++ */ ++ chr->write_blocked = true; ++ return 0; ++ } + } + } else { + /* XXX: indicate an error ? */ +-- +1.7.3.2 + diff --git a/0012-virtio-console-Enable-port-throttling-when-chardev-i.patch b/0012-virtio-console-Enable-port-throttling-when-chardev-i.patch new file mode 100644 index 0000000..90f7629 --- /dev/null +++ b/0012-virtio-console-Enable-port-throttling-when-chardev-i.patch @@ -0,0 +1,48 @@ +>From 94e8b44e4fdfbf312e54b78ca7bbb95271cc83ae Mon Sep 17 00:00:00 2001 +From: Amit Shah +Date: Mon, 21 Mar 2011 22:06:41 +0100 +Subject: [PATCH 12/17] virtio-console: Enable port throttling when chardev is slow to consume data + +When a chardev indicates it can't accept more data, we tell the +virtio-serial code to stop sending us any more data till we tell +otherwise. This helps in guests continuing to run normally while the vq +keeps getting full and eventually the guest stops queueing more data. +As soon as the chardev indicates it can accept more data, start pushing! + +Signed-off-by: Amit Shah +--- + hw/virtio-console.c | 11 +++++++++++ + 1 files changed, 11 insertions(+), 0 deletions(-) + +diff --git a/hw/virtio-console.c b/hw/virtio-console.c +index 22cf28c..eecbdf7 100644 +--- a/hw/virtio-console.c ++++ b/hw/virtio-console.c +@@ -18,6 +18,16 @@ typedef struct VirtConsole { + CharDriverState *chr; + } VirtConsole; + ++/* ++ * Callback function that's called from chardevs when backend becomes ++ * writable. ++ */ ++static void chr_write_unblocked(void *opaque) ++{ ++ VirtConsole *vcon = opaque; ++ ++ virtio_serial_throttle_port(&vcon->port, false); ++} + + /* Callback function that's called when the guest sends us data */ + static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len) +@@ -61,6 +71,7 @@ static const QemuChrHandlers chr_handlers = { + .fd_can_read = chr_can_read, + .fd_read = chr_read, + .fd_event = chr_event, ++ .fd_write_unblocked = chr_write_unblocked, + }; + + static int generic_port_init(VirtConsole *vcon, VirtIOSerialDevice *dev) +-- +1.7.3.2 + diff --git a/0013-spice-qemu-char.c-add-throttling.patch b/0013-spice-qemu-char.c-add-throttling.patch new file mode 100644 index 0000000..34cb283 --- /dev/null +++ b/0013-spice-qemu-char.c-add-throttling.patch @@ -0,0 +1,133 @@ +>From 06ad256d2939aea4086428dcb5e5e7d5f86eb335 Mon Sep 17 00:00:00 2001 +From: Alon Levy +Date: Tue, 22 Mar 2011 12:27:59 +0200 +Subject: [PATCH 13/17] spice-qemu-char.c: add throttling + +BZ: 672191 + +upstream: not submitted (explained below) + +Adds throttling support to spicevmc chardev. Uses a timer to avoid recursing: +1. spice-server: reds.c: read_from_vdi_port +2. qemu: spice-qemu-char.c: vmc_read +3. chr_write_unblocked + (calls virtio_serial_throttle_port(port, false)) +4. qemu: virtio ... +5. qemu: spice-qemu-char.c: spice_chr_write +6. qemu: spice-qemu-char.c: wakeup (calls into spice-server) +7. spice-server: ... +8. qemu: spice-qemu-char.c: vmc_read + +Instead, in vmc_read if we were throttled and we are just about to return +all the bytes we will set a timer to be triggered immediately to call +chr_write_unblocked. Then we return after 2 above, and 3 is called from the +timer callback. This also means we can later remove some ugly recursion protection +from spice-server. + +The other tricky point in this patch is not returning the leftover chunk twice. +When we throttle, by definition we have data that spice server didn't consume. +It is being kept by virtio-serial, and by us. The next vmc_read callback needs +to not return it, but just do unthrottling. Then virtio will give us the remaining +chunk as usual in spice_chr_write, and we will pass it to spice server in the +next vmc_read. + +This patch relies on Amit's series to expose throttling to chardev's, which +was not accepted upstream, and will not be accepted upstream until the mainloop +is reworked to use glib. +--- + spice-qemu-char.c | 39 +++++++++++++++++++++++++++++++++++---- + 1 files changed, 35 insertions(+), 4 deletions(-) + +diff --git a/spice-qemu-char.c b/spice-qemu-char.c +index 517f337..91467d5 100644 +--- a/spice-qemu-char.c ++++ b/spice-qemu-char.c +@@ -1,4 +1,6 @@ + #include "config-host.h" ++#include "qemu-common.h" ++#include "qemu-timer.h" + #include "trace.h" + #include "ui/qemu-spice.h" + #include +@@ -25,6 +27,7 @@ typedef struct SpiceCharDriver { + uint8_t *datapos; + ssize_t bufsize, datalen; + uint32_t debug; ++ QEMUTimer *unblock_timer; + } SpiceCharDriver; + + static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len) +@@ -51,6 +54,17 @@ static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len) + return out; + } + ++static void spice_chr_unblock(void *opaque) ++{ ++ SpiceCharDriver *scd = opaque; ++ ++ if (scd->chr->chr_write_unblocked == NULL) { ++ dprintf(scd, 1, "%s: backend doesn't support unthrottling.\n", __func__); ++ return; ++ } ++ scd->chr->chr_write_unblocked(scd->chr->handler_opaque); ++} ++ + static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len) + { + SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin); +@@ -62,9 +76,16 @@ static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len) + scd->datapos += bytes; + scd->datalen -= bytes; + assert(scd->datalen >= 0); +- if (scd->datalen == 0) { +- scd->datapos = 0; +- } ++ } ++ if (scd->datalen == 0 && scd->chr->write_blocked) { ++ dprintf(scd, 1, "%s: unthrottling (%d)\n", __func__, bytes); ++ scd->chr->write_blocked = false; ++ /* ++ * set a timer instead of calling scd->chr->chr_write_unblocked directly, ++ * because that will call back into spice_chr_write (see ++ * virtio-console.c:chr_write_unblocked), which is unwanted. ++ */ ++ qemu_mod_timer(scd->unblock_timer, 0); + } + trace_spice_vmc_read(bytes, len); + return bytes; +@@ -107,6 +128,7 @@ static void vmc_unregister_interface(SpiceCharDriver *scd) + static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len) + { + SpiceCharDriver *s = chr->opaque; ++ int read_bytes; + + dprintf(s, 2, "%s: %d\n", __func__, len); + vmc_register_interface(s); +@@ -119,7 +141,15 @@ static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len) + s->datapos = s->buffer; + s->datalen = len; + spice_server_char_device_wakeup(&s->sin); +- return len; ++ read_bytes = len - s->datalen; ++ if (read_bytes != len) { ++ dprintf(s, 1, "%s: throttling: %d < %d (%zd)\n", __func__, ++ read_bytes, len, s->bufsize); ++ s->chr->write_blocked = true; ++ /* We'll get passed in the unconsumed data with the next call */ ++ s->datalen = 0; ++ } ++ return read_bytes; + } + + static void spice_chr_close(struct CharDriverState *chr) +@@ -183,6 +213,7 @@ CharDriverState *qemu_chr_open_spice(QemuOpts *opts) + chr->opaque = s; + chr->chr_write = spice_chr_write; + chr->chr_close = spice_chr_close; ++ s->unblock_timer = qemu_new_timer(vm_clock, spice_chr_unblock, s); + + qemu_chr_generic_open(chr); + +-- +1.7.3.2 + diff --git a/0014-spice-qemu-char.c-remove-intermediate-buffer.patch b/0014-spice-qemu-char.c-remove-intermediate-buffer.patch new file mode 100644 index 0000000..2f56ce6 --- /dev/null +++ b/0014-spice-qemu-char.c-remove-intermediate-buffer.patch @@ -0,0 +1,71 @@ +>From 6ce8a141a37387a5138d0361cbe92885130010fe Mon Sep 17 00:00:00 2001 +From: Alon Levy +Date: Tue, 22 Mar 2011 12:28:00 +0200 +Subject: [PATCH 14/17] spice-qemu-char.c: remove intermediate buffer + +BZ: 672191 +upstream: not submitted (explained below) + +virtio-serial's buffer is valid when it calls us, and we don't +access it otherwise: vmc_read is only called in response to wakeup, +or else we set datalen=0 and throttle. Then vmc_read is called back, +we return 0 (not accessing the buffer) and set the timer to unthrottle. + +Also make datalen int and not ssize_t (to fit spice_chr_write signature). + +This relied on the previous patch that introduces throttling, which +can't go upstream right now as explained in that patch. +--- + spice-qemu-char.c | 18 ++++++------------ + 1 files changed, 6 insertions(+), 12 deletions(-) + +diff --git a/spice-qemu-char.c b/spice-qemu-char.c +index 91467d5..ed7851e 100644 +--- a/spice-qemu-char.c ++++ b/spice-qemu-char.c +@@ -23,9 +23,8 @@ typedef struct SpiceCharDriver { + SpiceCharDeviceInstance sin; + char *subtype; + bool active; +- uint8_t *buffer; +- uint8_t *datapos; +- ssize_t bufsize, datalen; ++ const uint8_t *datapos; ++ int datalen; + uint32_t debug; + QEMUTimer *unblock_timer; + } SpiceCharDriver; +@@ -70,7 +69,7 @@ static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len) + SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin); + int bytes = MIN(len, scd->datalen); + +- dprintf(scd, 2, "%s: %p %d/%d/%zd\n", __func__, scd->datapos, len, bytes, scd->datalen); ++ dprintf(scd, 2, "%s: %p %d/%d/%d\n", __func__, scd->datapos, len, bytes, scd->datalen); + if (bytes > 0) { + memcpy(buf, scd->datapos, bytes); + scd->datapos += bytes; +@@ -133,18 +132,13 @@ static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len) + dprintf(s, 2, "%s: %d\n", __func__, len); + vmc_register_interface(s); + assert(s->datalen == 0); +- if (s->bufsize < len) { +- s->bufsize = len; +- s->buffer = qemu_realloc(s->buffer, s->bufsize); +- } +- memcpy(s->buffer, buf, len); +- s->datapos = s->buffer; ++ s->datapos = buf; + s->datalen = len; + spice_server_char_device_wakeup(&s->sin); + read_bytes = len - s->datalen; + if (read_bytes != len) { +- dprintf(s, 1, "%s: throttling: %d < %d (%zd)\n", __func__, +- read_bytes, len, s->bufsize); ++ dprintf(s, 1, "%s: throttling: %d < %d\n", __func__, ++ read_bytes, len); + s->chr->write_blocked = true; + /* We'll get passed in the unconsumed data with the next call */ + s->datalen = 0; +-- +1.7.3.2 + diff --git a/0015-chardev-Allow-frontends-to-notify-backends-of-guest-.patch b/0015-chardev-Allow-frontends-to-notify-backends-of-guest-.patch new file mode 100644 index 0000000..bae63b8 --- /dev/null +++ b/0015-chardev-Allow-frontends-to-notify-backends-of-guest-.patch @@ -0,0 +1,76 @@ +>From c8cb28f0791ab38945c7facb5a63e445b4b6f41f Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Fri, 18 Mar 2011 15:23:21 +0100 +Subject: [PATCH 15/17] chardev: Allow frontends to notify backends of guest open / close + +Some frontends know when the guest has opened the "channel" and is actively +listening to it, for example virtio-serial. This patch adds 2 new qemu-chardev +functions which can be used by frontends to signal guest open / close, and +allows interested backends to listen to this. + +Signed-off-by: Hans de Goede +--- + qemu-char.c | 17 +++++++++++++++++ + qemu-char.h | 4 ++++ + 2 files changed, 21 insertions(+), 0 deletions(-) + +diff --git a/qemu-char.c b/qemu-char.c +index 2ef972f..d52eb51 100644 +--- a/qemu-char.c ++++ b/qemu-char.c +@@ -507,6 +507,9 @@ static CharDriverState *qemu_chr_open_mux(CharDriverState *drv) + chr->chr_write = mux_chr_write; + chr->chr_update_read_handler = mux_chr_update_read_handler; + chr->chr_accept_input = mux_chr_accept_input; ++ /* Frontend guest-open / -close notification is not support with muxes */ ++ chr->chr_guest_open = NULL; ++ chr->chr_guest_close = NULL; + + /* Muxes are always open on creation */ + qemu_chr_generic_open(chr); +@@ -2712,6 +2715,20 @@ void qemu_chr_set_echo(struct CharDriverState *chr, bool echo) + } + } + ++void qemu_chr_guest_open(struct CharDriverState *chr) ++{ ++ if (chr->chr_guest_open) { ++ chr->chr_guest_open(chr); ++ } ++} ++ ++void qemu_chr_guest_close(struct CharDriverState *chr) ++{ ++ if (chr->chr_guest_close) { ++ chr->chr_guest_close(chr); ++ } ++} ++ + void qemu_chr_close(CharDriverState *chr) + { + QTAILQ_REMOVE(&chardevs, chr, next); +diff --git a/qemu-char.h b/qemu-char.h +index bf06da0..f3b9bf4 100644 +--- a/qemu-char.h ++++ b/qemu-char.h +@@ -69,6 +69,8 @@ struct CharDriverState { + void (*chr_close)(struct CharDriverState *chr); + void (*chr_accept_input)(struct CharDriverState *chr); + void (*chr_set_echo)(struct CharDriverState *chr, bool echo); ++ void (*chr_guest_open)(struct CharDriverState *chr); ++ void (*chr_guest_close)(struct CharDriverState *chr); + void *opaque; + QEMUBH *bh; + char *label; +@@ -91,6 +93,8 @@ CharDriverState *qemu_chr_open_opts(QemuOpts *opts, + void (*init)(struct CharDriverState *s)); + CharDriverState *qemu_chr_open(const char *label, const char *filename, void (*init)(struct CharDriverState *s)); + void qemu_chr_set_echo(struct CharDriverState *chr, bool echo); ++void qemu_chr_guest_open(struct CharDriverState *chr); ++void qemu_chr_guest_close(struct CharDriverState *chr); + void qemu_chr_close(CharDriverState *chr); + void qemu_chr_printf(CharDriverState *s, const char *fmt, ...) + GCC_FMT_ATTR(2, 3); +-- +1.7.3.2 + diff --git a/0016-virtio-console-notify-backend-of-guest-open-close.patch b/0016-virtio-console-notify-backend-of-guest-open-close.patch new file mode 100644 index 0000000..0b3e3f8 --- /dev/null +++ b/0016-virtio-console-notify-backend-of-guest-open-close.patch @@ -0,0 +1,49 @@ +>From 3baf76e384c04f58f032632c078860d66c8c9db3 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Fri, 18 Mar 2011 15:30:45 +0100 +Subject: [PATCH 16/17] virtio-console: notify backend of guest open / close + +Signed-off-by: Hans de Goede +--- + hw/virtio-console.c | 18 ++++++++++++++++++ + 1 files changed, 18 insertions(+), 0 deletions(-) + +diff --git a/hw/virtio-console.c b/hw/virtio-console.c +index eecbdf7..828a1a3 100644 +--- a/hw/virtio-console.c ++++ b/hw/virtio-console.c +@@ -37,6 +37,22 @@ static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len) + return qemu_chr_write(vcon->chr, buf, len); + } + ++/* Callback function that's called when the guest opens the port */ ++static void guest_open(VirtIOSerialPort *port) ++{ ++ VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); ++ ++ qemu_chr_guest_open(vcon->chr); ++} ++ ++/* Callback function that's called when the guest closes the port */ ++static void guest_close(VirtIOSerialPort *port) ++{ ++ VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); ++ ++ qemu_chr_guest_close(vcon->chr); ++} ++ + /* Readiness of the guest to accept data on a port */ + static int chr_can_read(void *opaque) + { +@@ -81,6 +97,8 @@ static int generic_port_init(VirtConsole *vcon, VirtIOSerialDevice *dev) + if (vcon->chr) { + qemu_chr_add_handlers(vcon->chr, &chr_handlers, vcon); + vcon->port.info->have_data = flush_buf; ++ vcon->port.info->guest_open = guest_open; ++ vcon->port.info->guest_close = guest_close; + } + return 0; + } +-- +1.7.3.2 + diff --git a/0017-spice-chardev-listen-to-frontend-guest-open-close.patch b/0017-spice-chardev-listen-to-frontend-guest-open-close.patch new file mode 100644 index 0000000..1944bbc --- /dev/null +++ b/0017-spice-chardev-listen-to-frontend-guest-open-close.patch @@ -0,0 +1,49 @@ +>From c169795bed5374f0071af201da2dd32b3c5a2417 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Fri, 18 Mar 2011 15:35:27 +0100 +Subject: [PATCH 17/17] spice-chardev: listen to frontend guest open / close + +Note the vmc_register_interface() in spice_chr_write is left in place +in case someone uses spice-chardev with a frontend which does not have +guest open / close notification. + +Signed-off-by: Hans de Goede +--- + spice-qemu-char.c | 14 ++++++++++++++ + 1 files changed, 14 insertions(+), 0 deletions(-) + +diff --git a/spice-qemu-char.c b/spice-qemu-char.c +index ed7851e..343146c 100644 +--- a/spice-qemu-char.c ++++ b/spice-qemu-char.c +@@ -155,6 +155,18 @@ static void spice_chr_close(struct CharDriverState *chr) + qemu_free(s); + } + ++static void spice_chr_guest_open(struct CharDriverState *chr) ++{ ++ SpiceCharDriver *s = chr->opaque; ++ vmc_register_interface(s); ++} ++ ++static void spice_chr_guest_close(struct CharDriverState *chr) ++{ ++ SpiceCharDriver *s = chr->opaque; ++ vmc_unregister_interface(s); ++} ++ + static void print_allowed_subtypes(void) + { + const char** psubtype; +@@ -207,6 +219,8 @@ CharDriverState *qemu_chr_open_spice(QemuOpts *opts) + chr->opaque = s; + chr->chr_write = spice_chr_write; + chr->chr_close = spice_chr_close; ++ chr->chr_guest_open = spice_chr_guest_open; ++ chr->chr_guest_close = spice_chr_guest_close; + s->unblock_timer = qemu_new_timer(vm_clock, spice_chr_unblock, s); + + qemu_chr_generic_open(chr); +-- +1.7.3.2 + diff --git a/0018-spice-qemu-char-Fix-flow-control-in-client-guest-dir.patch b/0018-spice-qemu-char-Fix-flow-control-in-client-guest-dir.patch new file mode 100644 index 0000000..a6c2445 --- /dev/null +++ b/0018-spice-qemu-char-Fix-flow-control-in-client-guest-dir.patch @@ -0,0 +1,56 @@ +>From 7a9e7aaa30abf42879d3f13b41679513045c31ec Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Tue, 22 Mar 2011 16:28:41 +0100 +Subject: [PATCH 18/18] spice-qemu-char: Fix flow control in client -> guest direction + +In the old spice-vmc device we used to have: +last_out = virtio_serial_write(&svc->port, p, MIN(len, VMC_MAX_HOST_WRITE)); +if (last_out > 0) + ... + +Now in the chardev backend we have: +last_out = MIN(len, VMC_MAX_HOST_WRITE); +qemu_chr_read(scd->chr, p, last_out); +if (last_out > 0) { + ... + +Which causes us to no longer detect if the virtio port is not ready +to receive data from us. chardev actually has a mechanism to detect this, +but it requires a separate call to qemu_chr_can_read, before calling +qemu_chr_read (which return void). + +This patch uses qemu_chr_can_read to fix the flow control from client to +guest. + +Signed-off-by: Hans de Goede +--- + spice-qemu-char.c | 11 +++++------ + 1 files changed, 5 insertions(+), 6 deletions(-) + +diff --git a/spice-qemu-char.c b/spice-qemu-char.c +index 343146c..def713a 100644 +--- a/spice-qemu-char.c ++++ b/spice-qemu-char.c +@@ -38,14 +38,13 @@ static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len) + + while (len > 0) { + last_out = MIN(len, VMC_MAX_HOST_WRITE); +- qemu_chr_read(scd->chr, p, last_out); +- if (last_out > 0) { +- out += last_out; +- len -= last_out; +- p += last_out; +- } else { ++ if (qemu_chr_can_read(scd->chr) < last_out) { + break; + } ++ qemu_chr_read(scd->chr, p, last_out); ++ out += last_out; ++ len -= last_out; ++ p += last_out; + } + + dprintf(scd, 3, "%s: %lu/%zd\n", __func__, out, len + out); +-- +1.7.3.2 + diff --git a/qemu.spec b/qemu.spec index fce1113..63c6162 100644 --- a/qemu.spec +++ b/qemu.spec @@ -1,7 +1,7 @@ Summary: QEMU is a FAST! processor emulator Name: qemu Version: 0.14.0 -Release: 5%{?dist} +Release: 6%{?dist} # Epoch because we pushed a qemu-1.0 package Epoch: 2 License: GPLv2+ and LGPLv2+ and BSD @@ -46,6 +46,20 @@ Patch20: 0001-qxl-spice-display-move-pipe-to-ssd.patch Patch21: 0002-qxl-implement-get_command-in-vga-mode-without-locks.patch Patch22: 0003-qxl-spice-remove-qemu_mutex_-un-lock_iothread-around.patch Patch23: 0004-hw-qxl-render-drop-cursor-locks-replace-with-pipe.patch +Patch24: 0005-char-Split-out-tcp-socket-close-code-in-a-separate-f.patch +Patch25: 0006-char-Add-a-QemuChrHandlers-struct-to-initialise-char.patch +Patch26: 0007-iohandlers-Add-enable-disable_write_fd_handler-funct.patch +Patch27: 0008-char-Add-framework-for-a-write-unblocked-callback.patch +Patch28: 0009-char-Update-send_all-to-handle-nonblocking-chardev-w.patch +Patch29: 0010-char-Equip-the-unix-tcp-backend-to-handle-nonblockin.patch +Patch30: 0011-char-Throttle-when-host-connection-is-down.patch +Patch31: 0012-virtio-console-Enable-port-throttling-when-chardev-i.patch +Patch32: 0013-spice-qemu-char.c-add-throttling.patch +Patch33: 0014-spice-qemu-char.c-remove-intermediate-buffer.patch +Patch34: 0015-chardev-Allow-frontends-to-notify-backends-of-guest-.patch +Patch35: 0016-virtio-console-notify-backend-of-guest-open-close.patch +Patch36: 0017-spice-chardev-listen-to-frontend-guest-open-close.patch +Patch37: 0018-spice-qemu-char-Fix-flow-control-in-client-guest-dir.patch BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) @@ -251,6 +265,20 @@ such as kvm_stat. %patch21 -p1 %patch22 -p1 %patch23 -p1 +%patch24 -p1 +%patch25 -p1 +%patch26 -p1 +%patch27 -p1 +%patch28 -p1 +%patch29 -p1 +%patch30 -p1 +%patch31 -p1 +%patch32 -p1 +%patch33 -p1 +%patch34 -p1 +%patch35 -p1 +%patch36 -p1 +%patch37 -p1 %build # By default we build everything, but allow x86 to build a minimal version @@ -585,6 +613,9 @@ fi %{_mandir}/man1/qemu-img.1* %changelog +* Mon Mar 28 2011 Justin M. Forbes - 2:0.14.0-6 +- Spice fixes for flow control. + * Tue Mar 22 2011 Dan HorĂ¡k - 2:0.14.0-5 - be more careful when removing the -g flag on s390