fc5c27b
>From a962453ed73a671f566cc94858dc21ab694cc85f Mon Sep 17 00:00:00 2001
3f1f296
From: Amit Shah <amit.shah@redhat.com>
3f1f296
Date: Mon, 21 Mar 2011 22:00:27 +0100
fc5c27b
Subject: [PATCH 05/28] char: Update send_all() to handle nonblocking chardev
3f1f296
 write requests
3f1f296
3f1f296
The send_all function is modified to return to the caller in case the
3f1f296
driver cannot handle any more data.  It returns -EAGAIN or
3f1f296
WSAEWOULDBLOCK on non-Windows and Windows platforms respectively.  This
3f1f296
is only done when the caller sets a callback function handler indicating
3f1f296
it's not interested in blocking till the driver has written out all the
3f1f296
data.
3f1f296
3f1f296
Currently there's no driver or caller that supports this.  Future
3f1f296
commits will add such capability.
3f1f296
3f1f296
Signed-off-by: Amit Shah <amit.shah@redhat.com>
3f1f296
---
3f1f296
 net/socket.c  |    4 +-
3f1f296
 qemu-char.c   |   80 ++++++++++++++++++++++++++++++++++++++++++++++++++++----
3f1f296
 qemu_socket.h |    2 +-
3f1f296
 3 files changed, 77 insertions(+), 9 deletions(-)
3f1f296
3f1f296
diff --git a/net/socket.c b/net/socket.c
13f703f
index 11fe5f3..14706fc 100644
3f1f296
--- a/net/socket.c
3f1f296
+++ b/net/socket.c
3f1f296
@@ -56,8 +56,8 @@ static ssize_t net_socket_receive(VLANClientState *nc, const uint8_t *buf, size_
3f1f296
     uint32_t len;
3f1f296
     len = htonl(size);
3f1f296
 
3f1f296
-    send_all(s->fd, (const uint8_t *)&len, sizeof(len));
3f1f296
-    return send_all(s->fd, buf, size);
3f1f296
+    send_all(NULL, s->fd, (const uint8_t *)&len, sizeof(len));
3f1f296
+    return send_all(NULL, s->fd, buf, size);
3f1f296
 }
3f1f296
 
3f1f296
 static ssize_t net_socket_receive_dgram(VLANClientState *nc, const uint8_t *buf, size_t size)
3f1f296
diff --git a/qemu-char.c b/qemu-char.c
13f703f
index bc07b20..5dbf063 100644
3f1f296
--- a/qemu-char.c
3f1f296
+++ b/qemu-char.c
5e10b14
@@ -513,7 +513,7 @@ static CharDriverState *qemu_chr_open_mux(CharDriverState *drv)
3f1f296
 
3f1f296
 
3f1f296
 #ifdef _WIN32
3f1f296
-int send_all(int fd, const void *buf, int len1)
3f1f296
+static int do_send(int fd, const void *buf, int len1, bool nonblock)
3f1f296
 {
3f1f296
     int ret, len;
3f1f296
 
5e10b14
@@ -521,9 +521,14 @@ int send_all(int fd, const void *buf, int len1)
3f1f296
     while (len > 0) {
3f1f296
         ret = send(fd, buf, len, 0);
3f1f296
         if (ret < 0) {
3f1f296
+            if (nonblock && len1 - len) {
3f1f296
+                return len1 - len;
3f1f296
+            }
3f1f296
             errno = WSAGetLastError();
3f1f296
             if (errno != WSAEWOULDBLOCK) {
3f1f296
                 return -1;
3f1f296
+            } else if (errno == WSAEWOULDBLOCK && nonblock) {
3f1f296
+                return WSAEWOULDBLOCK;
3f1f296
             }
3f1f296
         } else if (ret == 0) {
3f1f296
             break;
5e10b14
@@ -537,7 +542,7 @@ int send_all(int fd, const void *buf, int len1)
3f1f296
 
3f1f296
 #else
3f1f296
 
3f1f296
-int send_all(int fd, const void *_buf, int len1)
3f1f296
+static int do_send(int fd, const void *_buf, int len1, bool nonblock)
3f1f296
 {
3f1f296
     int ret, len;
3f1f296
     const uint8_t *buf = _buf;
5e10b14
@@ -546,8 +551,15 @@ int send_all(int fd, const void *_buf, int len1)
3f1f296
     while (len > 0) {
3f1f296
         ret = write(fd, buf, len);
3f1f296
         if (ret < 0) {
3f1f296
-            if (errno != EINTR && errno != EAGAIN)
3f1f296
+            if (nonblock && len1 - len) {
3f1f296
+                return len1 - len;
3f1f296
+            }
3f1f296
+            if (errno == EAGAIN && nonblock) {
3f1f296
+                return -EAGAIN;
3f1f296
+            }
3f1f296
+            if (errno != EINTR && errno != EAGAIN) {
3f1f296
                 return -1;
3f1f296
+            }
3f1f296
         } else if (ret == 0) {
3f1f296
             break;
3f1f296
         } else {
5e10b14
@@ -559,6 +571,55 @@ int send_all(int fd, const void *_buf, int len1)
3f1f296
 }
3f1f296
 #endif /* !_WIN32 */
3f1f296
 
3f1f296
+int send_all(CharDriverState *chr, int fd, const void *_buf, int len1)
3f1f296
+{
3f1f296
+    int ret, eagain_errno;
3f1f296
+    bool nonblock;
3f1f296
+
3f1f296
+    if (chr && chr->write_blocked) {
3f1f296
+        /*
3f1f296
+         * We don't handle this situation: the caller should not send
3f1f296
+         * us data while we're blocked.
3f1f296
+         *
3f1f296
+         * We could buffer this data here but that'll only encourage
3f1f296
+         * bad behaviour on part of the callers.
3f1f296
+         *
3f1f296
+         * Also, the data already in fd's buffers isn't easily
3f1f296
+         * migratable.  If we want full migration support, all the
3f1f296
+         * data landing here needs to be buffered and on migration,
3f1f296
+         * anything that's unsent needs to be transferred to the
3f1f296
+         * dest. machine (which again isn't a very good way of solving
3f1f296
+         * the problem, as the src may become writable just during
3f1f296
+         * migration and the reader could receive some data twice,
3f1f296
+         * essentially corrupting the data).
3f1f296
+         */
3f1f296
+        abort();
3f1f296
+    }
3f1f296
+
3f1f296
+    nonblock = false;
3f1f296
+    /*
3f1f296
+     * Ensure the char backend is able to receive and handle the
3f1f296
+     * 'write unblocked' event before we turn on nonblock support.
3f1f296
+     */
3f1f296
+    if (chr && chr->chr_enable_write_fd_handler && chr->chr_write_unblocked) {
3f1f296
+        nonblock = true;
3f1f296
+    }
3f1f296
+    ret = do_send(fd, _buf, len1, nonblock);
3f1f296
+
3f1f296
+#ifdef _WIN32
3f1f296
+    eagain_errno = WSAEWOULDBLOCK;
3f1f296
+#else
3f1f296
+    eagain_errno = -EAGAIN;
3f1f296
+#endif
3f1f296
+
3f1f296
+    if (nonblock && (ret == eagain_errno || (ret >= 0 && ret < len1))) {
3f1f296
+        /* Update fd handler to wake up when chr becomes writable */
3f1f296
+        chr->chr_enable_write_fd_handler(chr);
3f1f296
+        chr->write_blocked = true;
3f1f296
+    }
3f1f296
+    return ret;
3f1f296
+}
3f1f296
+
3f1f296
 #ifndef _WIN32
3f1f296
 
3f1f296
 typedef struct {
5e10b14
@@ -572,7 +633,7 @@ static int stdio_nb_clients = 0;
3f1f296
 static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
3f1f296
 {
3f1f296
     FDCharDriver *s = chr->opaque;
3f1f296
-    return send_all(s->fd_out, buf, len);
3f1f296
+    return send_all(chr, s->fd_out, buf, len);
3f1f296
 }
3f1f296
 
3f1f296
 static int fd_chr_read_poll(void *opaque)
5e10b14
@@ -897,7 +958,7 @@ static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
3f1f296
         pty_chr_update_read_handler(chr);
3f1f296
         return 0;
3f1f296
     }
3f1f296
-    return send_all(s->fd, buf, len);
3f1f296
+    return send_all(chr, s->fd, buf, len);
3f1f296
 }
3f1f296
 
3f1f296
 static int pty_chr_read_poll(void *opaque)
13f703f
@@ -1979,8 +2040,15 @@ static void tcp_closed(void *opaque)
3f1f296
 static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
3f1f296
 {
3f1f296
     TCPCharDriver *s = chr->opaque;
3f1f296
+
3f1f296
     if (s->connected) {
3f1f296
-        return send_all(s->fd, buf, len);
3f1f296
+        int ret;
3f1f296
+
3f1f296
+        ret = send_all(chr, s->fd, buf, len);
3f1f296
+        if (ret == -1 && errno == EPIPE) {
3f1f296
+            tcp_closed(chr);
3f1f296
+        }
3f1f296
+        return ret;
3f1f296
     } else {
3f1f296
         /* XXX: indicate an error ? */
3f1f296
         return len;
3f1f296
diff --git a/qemu_socket.h b/qemu_socket.h
3f1f296
index 180e4db..6f453e5 100644
3f1f296
--- a/qemu_socket.h
3f1f296
+++ b/qemu_socket.h
3f1f296
@@ -36,7 +36,7 @@ int inet_aton(const char *cp, struct in_addr *ia);
3f1f296
 int qemu_socket(int domain, int type, int protocol);
3f1f296
 int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen);
3f1f296
 void socket_set_nonblock(int fd);
3f1f296
-int send_all(int fd, const void *buf, int len1);
3f1f296
+int send_all(CharDriverState *chr, int fd, const void *buf, int len1);
3f1f296
 
3f1f296
 /* New, ipv6-ready socket helper functions, see qemu-sockets.c */
3f1f296
 int inet_listen_opts(QemuOpts *opts, int port_offset);
3f1f296
-- 
3f1f296
1.7.5.1
3f1f296