[qemu] Spice fixes for flow control

Justin M. Forbes jforbes at fedoraproject.org
Mon Mar 28 18:54:45 UTC 2011


commit 252f3af86eaa15b522dffa6fdfa1d81fe8851c94
Author: Justin M. Forbes <jforbes at redhat.com>
Date:   Mon Mar 28 13:54:26 2011 -0500

    Spice fixes for flow control

 ...out-tcp-socket-close-code-in-a-separate-f.patch |   56 ++
 ...QemuChrHandlers-struct-to-initialise-char.patch |  673 ++++++++++++++++++++
 ...Add-enable-disable_write_fd_handler-funct.patch |   76 +++
 ...-framework-for-a-write-unblocked-callback.patch |   62 ++
 ...-send_all-to-handle-nonblocking-chardev-w.patch |  197 ++++++
 ...the-unix-tcp-backend-to-handle-nonblockin.patch |   80 +++
 ...har-Throttle-when-host-connection-is-down.patch |   56 ++
 ...ole-Enable-port-throttling-when-chardev-i.patch |   48 ++
 0013-spice-qemu-char.c-add-throttling.patch        |  133 ++++
 ...ce-qemu-char.c-remove-intermediate-buffer.patch |   71 ++
 ...ow-frontends-to-notify-backends-of-guest-.patch |   76 +++
 ...onsole-notify-backend-of-guest-open-close.patch |   49 ++
 ...ardev-listen-to-frontend-guest-open-close.patch |   49 ++
 ...char-Fix-flow-control-in-client-guest-dir.patch |   56 ++
 qemu.spec                                          |   33 +-
 15 files changed, 1714 insertions(+), 1 deletions(-)
---
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 <amit.shah at redhat.com>
+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 <amit.shah at redhat.com>
+---
+ 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 <amit.shah at redhat.com>
+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 <amit.shah at redhat.com>
+---
+ 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 <stdbool.h>
+ #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 <amit.shah at redhat.com>
+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 <amit.shah at redhat.com>
+---
+ 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 <amit.shah at redhat.com>
+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 <amit.shah at redhat.com>
+---
+ 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 <amit.shah at redhat.com>
+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 <amit.shah at redhat.com>
+---
+ 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 <amit.shah at redhat.com>
+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 <amit.shah at redhat.com>
+---
+ 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 <amit.shah at redhat.com>
+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 <amit.shah at redhat.com>
+---
+ 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 <amit.shah at redhat.com>
+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 <amit.shah at redhat.com>
+---
+ 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 <alevy at redhat.com>
+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 <spice.h>
+@@ -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 <alevy at redhat.com>
+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 <hdegoede at redhat.com>
+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 <hdegoede at redhat.com>
+---
+ 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 <hdegoede at redhat.com>
+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 <hdegoede at redhat.com>
+---
+ 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 <hdegoede at redhat.com>
+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 <hdegoede at redhat.com>
+---
+ 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 <hdegoede at redhat.com>
+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 <hdegoede at redhat.com>
+---
+ 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 <jforbes at redhat.com> - 2:0.14.0-6
+- Spice fixes for flow control.
+
 * Tue Mar 22 2011 Dan Horák <dan[at]danny.cz> - 2:0.14.0-5
 - be more careful when removing the -g flag on s390
 


More information about the scm-commits mailing list