Blame 0005-char-Update-send_all-to-handle-nonblocking-chardev-w.patch

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