Blob Blame History Raw
From 9fd3a478a3823258516f06201fa681e07dce1781 Mon Sep 17 00:00:00 2001
From: Amit Shah <amit.shah@redhat.com>
Date: Mon, 21 Mar 2011 22:02:47 +0100
Subject: [PATCH] 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@redhat.com>
Signed-off-by: Cole Robinson <crobinso@redhat.com>
---
 qemu-char.c | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/qemu-char.c b/qemu-char.c
index 3d6e2f8..18e980d 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -105,6 +105,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);
+}
+
 void qemu_chr_be_event(CharDriverState *s, int event)
 {
     /* Keep track if the char device is open */
@@ -126,6 +139,9 @@ static void qemu_chr_fire_open_event(void *opaque)
 {
     CharDriverState *s = opaque;
     qemu_chr_be_event(s, CHR_EVENT_OPENED);
+    if (s->write_blocked) {
+        char_write_unblocked(s);
+    }
     qemu_free_timer(s->open_timer);
     s->open_timer = NULL;
 }
@@ -2245,6 +2261,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;
+            }
         }
         return ret;
     } else {