f375e62
From 0bb331d124049a9157c08bb1d3572493e6691f05 Mon Sep 17 00:00:00 2001
d4cdad5
From: Amit Shah <amit.shah@redhat.com>
d4cdad5
Date: Mon, 21 Mar 2011 22:00:27 +0100
5544c1b
Subject: [PATCH] char: Update send_all() to handle nonblocking chardev write
5544c1b
 requests
d4cdad5
d4cdad5
The send_all function is modified to return to the caller in case the
d4cdad5
driver cannot handle any more data.  It returns -EAGAIN or
d4cdad5
WSAEWOULDBLOCK on non-Windows and Windows platforms respectively.  This
d4cdad5
is only done when the caller sets a callback function handler indicating
d4cdad5
it's not interested in blocking till the driver has written out all the
d4cdad5
data.
d4cdad5
d4cdad5
Currently there's no driver or caller that supports this.  Future
d4cdad5
commits will add such capability.
d4cdad5
d4cdad5
Signed-off-by: Amit Shah <amit.shah@redhat.com>
bd56df9
Signed-off-by: Cole Robinson <crobinso@redhat.com>
d4cdad5
---
b6dd5ac
 qemu-char.c   | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++------
b6dd5ac
 qemu_socket.h |  2 +-
5544c1b
 2 files changed, 64 insertions(+), 7 deletions(-)
d4cdad5
d4cdad5
diff --git a/qemu-char.c b/qemu-char.c
5544c1b
index b5266d1..b46cc97 100644
d4cdad5
--- a/qemu-char.c
d4cdad5
+++ b/qemu-char.c
329b588
@@ -508,7 +508,7 @@ static CharDriverState *qemu_chr_open_mux(CharDriverState *drv)
329b588
 
329b588
 
d4cdad5
 #ifdef _WIN32
d4cdad5
-int send_all(int fd, const void *buf, int len1)
d4cdad5
+static int do_send(int fd, const void *buf, int len1, bool nonblock)
d4cdad5
 {
d4cdad5
     int ret, len;
329b588
 
329b588
@@ -516,9 +516,14 @@ int send_all(int fd, const void *buf, int len1)
d4cdad5
     while (len > 0) {
d4cdad5
         ret = send(fd, buf, len, 0);
d4cdad5
         if (ret < 0) {
d4cdad5
+            if (nonblock && len1 - len) {
d4cdad5
+                return len1 - len;
d4cdad5
+            }
d4cdad5
             errno = WSAGetLastError();
d4cdad5
             if (errno != WSAEWOULDBLOCK) {
d4cdad5
                 return -1;
d4cdad5
+            } else if (errno == WSAEWOULDBLOCK && nonblock) {
d4cdad5
+                return WSAEWOULDBLOCK;
d4cdad5
             }
d4cdad5
         } else if (ret == 0) {
d4cdad5
             break;
329b588
@@ -532,7 +537,7 @@ int send_all(int fd, const void *buf, int len1)
329b588
 
d4cdad5
 #else
329b588
 
d4cdad5
-int send_all(int fd, const void *_buf, int len1)
d4cdad5
+static int do_send(int fd, const void *_buf, int len1, bool nonblock)
d4cdad5
 {
d4cdad5
     int ret, len;
d4cdad5
     const uint8_t *buf = _buf;
329b588
@@ -541,8 +546,15 @@ int send_all(int fd, const void *_buf, int len1)
d4cdad5
     while (len > 0) {
d4cdad5
         ret = write(fd, buf, len);
d4cdad5
         if (ret < 0) {
d4cdad5
-            if (errno != EINTR && errno != EAGAIN)
d4cdad5
+            if (nonblock && len1 - len) {
d4cdad5
+                return len1 - len;
d4cdad5
+            }
d4cdad5
+            if (errno == EAGAIN && nonblock) {
d4cdad5
+                return -EAGAIN;
d4cdad5
+            }
d4cdad5
+            if (errno != EINTR && errno != EAGAIN) {
d4cdad5
                 return -1;
d4cdad5
+            }
d4cdad5
         } else if (ret == 0) {
d4cdad5
             break;
d4cdad5
         } else {
329b588
@@ -557,6 +569,44 @@ int send_all(int fd, const void *_buf, int len1)
d4cdad5
 #define STDIO_MAX_CLIENTS 1
d4cdad5
 static int stdio_nb_clients;
329b588
 
d4cdad5
+int send_all(CharDriverState *chr, int fd, const void *_buf, int len1)
d4cdad5
+{
d4cdad5
+    int ret, eagain_errno;
d4cdad5
+    bool nonblock;
d4cdad5
+
d4cdad5
+    if (chr && chr->write_blocked) {
d4cdad5
+        /*
d4cdad5
+         * The caller should not send us data while we're blocked,
d4cdad5
+         * but this can happen when multiple writers are woken at once,
d4cdad5
+         * so simply return -EAGAIN.
d4cdad5
+         */
d4cdad5
+        return -EAGAIN;
d4cdad5
+    }
d4cdad5
+
d4cdad5
+    nonblock = false;
d4cdad5
+    /*
d4cdad5
+     * Ensure the char backend is able to receive and handle the
d4cdad5
+     * 'write unblocked' event before we turn on nonblock support.
d4cdad5
+     */
d4cdad5
+    if (chr && chr->chr_enable_write_fd_handler && chr->chr_write_unblocked) {
d4cdad5
+        nonblock = true;
d4cdad5
+    }
d4cdad5
+    ret = do_send(fd, _buf, len1, nonblock);
d4cdad5
+
d4cdad5
+#ifdef _WIN32
d4cdad5
+    eagain_errno = WSAEWOULDBLOCK;
d4cdad5
+#else
d4cdad5
+    eagain_errno = -EAGAIN;
d4cdad5
+#endif
d4cdad5
+
d4cdad5
+    if (nonblock && (ret == eagain_errno || (ret >= 0 && ret < len1))) {
d4cdad5
+        /* Update fd handler to wake up when chr becomes writable */
d4cdad5
+        chr->chr_enable_write_fd_handler(chr);
d4cdad5
+        chr->write_blocked = true;
d4cdad5
+    }
d4cdad5
+    return ret;
d4cdad5
+}
d4cdad5
+
d4cdad5
 #ifndef _WIN32
329b588
 
d4cdad5
 typedef struct {
329b588
@@ -568,7 +618,7 @@ typedef struct {
d4cdad5
 static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
d4cdad5
 {
d4cdad5
     FDCharDriver *s = chr->opaque;
d4cdad5
-    return send_all(s->fd_out, buf, len);
d4cdad5
+    return send_all(chr, s->fd_out, buf, len);
d4cdad5
 }
329b588
 
d4cdad5
 static int fd_chr_read_poll(void *opaque)
329b588
@@ -887,7 +937,7 @@ static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
d4cdad5
         pty_chr_update_read_handler(chr);
d4cdad5
         return 0;
d4cdad5
     }
d4cdad5
-    return send_all(s->fd, buf, len);
d4cdad5
+    return send_all(chr, s->fd, buf, len);
d4cdad5
 }
329b588
 
d4cdad5
 static int pty_chr_read_poll(void *opaque)
5544c1b
@@ -2174,8 +2224,15 @@ static void tcp_closed(void *opaque)
d4cdad5
 static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
d4cdad5
 {
d4cdad5
     TCPCharDriver *s = chr->opaque;
d4cdad5
+
d4cdad5
     if (s->connected) {
d4cdad5
-        return send_all(s->fd, buf, len);
d4cdad5
+        int ret;
d4cdad5
+
d4cdad5
+        ret = send_all(chr, s->fd, buf, len);
d4cdad5
+        if (ret == -1 && errno == EPIPE) {
d4cdad5
+            tcp_closed(chr);
d4cdad5
+        }
d4cdad5
+        return ret;
d4cdad5
     } else {
5544c1b
         /* XXX: indicate an error ? */
5544c1b
         return len;
d4cdad5
diff --git a/qemu_socket.h b/qemu_socket.h
5544c1b
index 3e8aee9..a537d86 100644
d4cdad5
--- a/qemu_socket.h
d4cdad5
+++ b/qemu_socket.h
329b588
@@ -36,7 +36,7 @@ int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen);
329b588
 int socket_set_cork(int fd, int v);
d4cdad5
 void socket_set_block(int fd);
d4cdad5
 void socket_set_nonblock(int fd);
d4cdad5
-int send_all(int fd, const void *buf, int len1);
d4cdad5
+int send_all(CharDriverState *chr, int fd, const void *buf, int len1);
329b588
 
5544c1b
 /* callback function for nonblocking connect
5544c1b
  * valid fd on success, negative error code on failure