Patchwork [v7,4/6] char: Add qemu_chr_write_nb() for nonblocking writes

login
register
mail settings
Submitter Amit Shah
Date May 4, 2010, 9:39 p.m.
Message ID <1273009170-17530-5-git-send-email-amit.shah@redhat.com>
Download mbox | patch
Permalink /patch/51693/
State New
Headers show

Comments

Amit Shah - May 4, 2010, 9:39 p.m.
For char devices whose backing files are open in non-blocking mode,
non-blocking writes can now be made using qemu_chr_write_nb().

For non-blocking chardevs, we can return -EAGAIN to callers of
qemu_chr_write_nb(). When the backend is ready to accept more data,
we can let the caller know via a callback.

-EAGAIN is returned only when the backend's file is non-blocking
and a callback is registered by the caller when invoking
qemu_chr_add_handlers().

In case a backend doesn't support non-blocking writes,
qemu_chr_write_nb() invokes qemu_chr_write().

Individual callers can update their code to add a callback handler,
call qemu_chr_write_nb() instead of qemu_chr_write() if they wish to
receive -EAGAIN notifications.

No backend currently supports non-blocking writes.

Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
 net/socket.c  |    4 ++--
 qemu-char.c   |   31 ++++++++++++++++++++++++-------
 qemu-char.h   |    8 ++++++++
 qemu_socket.h |    3 ++-
 4 files changed, 36 insertions(+), 10 deletions(-)
Anthony Liguori - May 5, 2010, 1:16 p.m.
On 05/04/2010 04:39 PM, Amit Shah wrote:
> For char devices whose backing files are open in non-blocking mode,
> non-blocking writes can now be made using qemu_chr_write_nb().
>
> For non-blocking chardevs, we can return -EAGAIN to callers of
> qemu_chr_write_nb(). When the backend is ready to accept more data,
> we can let the caller know via a callback.
>
> -EAGAIN is returned only when the backend's file is non-blocking
> and a callback is registered by the caller when invoking
> qemu_chr_add_handlers().
>
> In case a backend doesn't support non-blocking writes,
> qemu_chr_write_nb() invokes qemu_chr_write().
>
> Individual callers can update their code to add a callback handler,
> call qemu_chr_write_nb() instead of qemu_chr_write() if they wish to
> receive -EAGAIN notifications.
>
> No backend currently supports non-blocking writes.
>
> Signed-off-by: Amit Shah<amit.shah@redhat.com>
> ---
>   net/socket.c  |    4 ++--
>   qemu-char.c   |   31 ++++++++++++++++++++++++-------
>   qemu-char.h   |    8 ++++++++
>   qemu_socket.h |    3 ++-
>   4 files changed, 36 insertions(+), 10 deletions(-)
>
> diff --git a/net/socket.c b/net/socket.c
> index 1c4e153..8a401e6 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(s->fd, (const uint8_t *)&len, sizeof(len), false);
> +    return send_all(s->fd, buf, size, false);
>   }
>
>   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 decf687..5e4dec3 100644
> --- a/qemu-char.c
> +++ b/qemu-char.c
> @@ -145,6 +145,16 @@ int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len)
>       return s->chr_write(s, buf, len);
>   }
>
> +ssize_t qemu_chr_write_nb(CharDriverState *s, const uint8_t *buf, size_t len)
> +{
> +    if (!s->nonblock) {
> +        /* Fallback to blocking write if no callback registered */
> +        return qemu_chr_write(s, buf, len);
> +    }
> +
> +    return s->chr_write_nb(s, buf, len);
> +}
>    

I really dislike the idea of adding another function for this.  Can you 
explain why you need this functionality for virtio-console and why this 
functionality isn't needed for everything else?

Regards,

Anthony Liguori

>   int qemu_chr_ioctl(CharDriverState *s, int cmd, void *arg)
>   {
>       if (!s->chr_ioctl)
> @@ -203,11 +213,15 @@ 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);
>
> +    /* We'll set this at connect-time */
> +    s->nonblock = false;
> +
>       /* We're connecting to an already opened device, so let's make sure we
>          also get the open event */
>       if (s->opened) {
> @@ -499,7 +513,7 @@ static CharDriverState *qemu_chr_open_mux(CharDriverState *drv)
>
>
>   #ifdef _WIN32
> -int send_all(int fd, const void *buf, int len1)
> +int send_all(int fd, const void *buf, int len1, bool nonblock)
>   {
>       int ret, len;
>
> @@ -526,7 +540,7 @@ int send_all(int fd, const void *buf, int len1)
>
>   #else
>
> -static int unix_write(int fd, const uint8_t *buf, int len1)
> +static int unix_write(int fd, const uint8_t *buf, int len1, bool nonblock)
>   {
>       int ret, len;
>
> @@ -540,6 +554,9 @@ static int unix_write(int fd, const uint8_t *buf, int len1)
>               if (len1 - len) {
>                   return len1 - len;
>               }
> +            if (errno == EAGAIN&&  nonblock) {
> +                return -EAGAIN;
> +            }
>               if (errno != EAGAIN) {
>                   return -1;
>               }
> @@ -553,9 +570,9 @@ static int unix_write(int fd, const uint8_t *buf, int len1)
>       return len1 - len;
>   }
>
> -int send_all(int fd, const void *buf, int len1)
> +int send_all(int fd, const void *buf, int len1, bool nonblock)
>   {
> -    return unix_write(fd, buf, len1);
> +    return unix_write(fd, buf, len1, nonblock);
>   }
>   #endif /* !_WIN32 */
>
> @@ -572,7 +589,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(s->fd_out, buf, len, false);
>   }
>
>   static int fd_chr_read_poll(void *opaque)
> @@ -884,7 +901,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(s->fd, buf, len, false);
>   }
>
>   static int pty_chr_read_poll(void *opaque)
> @@ -1949,7 +1966,7 @@ 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);
> +        return send_all(s->fd, buf, len, false);
>       } else {
>           /* XXX: indicate an error ? */
>           return len;
> diff --git a/qemu-char.h b/qemu-char.h
> index eacb3b9..52f9ef1 100644
> --- a/qemu-char.h
> +++ b/qemu-char.h
> @@ -1,6 +1,8 @@
>   #ifndef QEMU_CHAR_H
>   #define QEMU_CHAR_H
>
> +#include<stdbool.h>
> +
>   #include "qemu-common.h"
>   #include "qemu-queue.h"
>   #include "qemu-option.h"
> @@ -53,12 +55,15 @@ typedef void IOEventHandler(void *opaque, int event);
>   struct CharDriverState {
>       void (*init)(struct CharDriverState *s);
>       int (*chr_write)(struct CharDriverState *s, const uint8_t *buf, int len);
> +    ssize_t (*chr_write_nb)(struct CharDriverState *s, const uint8_t *buf,
> +                            size_t len);
>       void (*chr_update_read_handler)(struct CharDriverState *s);
>       int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg);
>       int (*get_msgfd)(struct CharDriverState *s);
>       IOEventHandler *chr_event;
>       IOCanReadHandler *chr_can_read;
>       IOReadHandler *chr_read;
> +    IOHandler *chr_write_unblocked;
>       void *handler_opaque;
>       void (*chr_send_event)(struct CharDriverState *chr, int event);
>       void (*chr_close)(struct CharDriverState *chr);
> @@ -68,6 +73,8 @@ struct CharDriverState {
>       char *label;
>       char *filename;
>       int opened;
> +    bool nonblock;
> +    bool write_blocked;
>       QTAILQ_ENTRY(CharDriverState) next;
>   };
>
> @@ -85,6 +92,7 @@ CharDriverState *qemu_chr_open(const char *label, const char *filename, void (*i
>   void qemu_chr_close(CharDriverState *chr);
>   void qemu_chr_printf(CharDriverState *s, const char *fmt, ...);
>   int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len);
> +ssize_t qemu_chr_write_nb(CharDriverState *s, const uint8_t *buf, size_t len);
>   void qemu_chr_send_event(CharDriverState *s, int event);
>   void qemu_chr_add_handlers(CharDriverState *s, const QemuChrHandlers *handlers,
>                              void *opaque);
> diff --git a/qemu_socket.h b/qemu_socket.h
> index 164ae3e..bdf878b 100644
> --- a/qemu_socket.h
> +++ b/qemu_socket.h
> @@ -23,6 +23,7 @@ int inet_aton(const char *cp, struct in_addr *ia);
>   #include<arpa/inet.h>
>   #include<netdb.h>
>   #include<sys/un.h>
> +#include<stdbool.h>
>
>   #define socket_error() errno
>   #define closesocket(s) close(s)
> @@ -35,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(int fd, const void *buf, int len1, bool nonblock);
>
>   /* New, ipv6-ready socket helper functions, see qemu-sockets.c */
>   int inet_listen_opts(QemuOpts *opts, int port_offset);
>
Amit Shah - May 5, 2010, 1:22 p.m.
On (Wed) May 05 2010 [08:16:37], Anthony Liguori wrote:
> On 05/04/2010 04:39 PM, Amit Shah wrote:
>> For char devices whose backing files are open in non-blocking mode,
>> non-blocking writes can now be made using qemu_chr_write_nb().
>>
>> For non-blocking chardevs, we can return -EAGAIN to callers of
>> qemu_chr_write_nb(). When the backend is ready to accept more data,
>> we can let the caller know via a callback.
>>
>> -EAGAIN is returned only when the backend's file is non-blocking
>> and a callback is registered by the caller when invoking
>> qemu_chr_add_handlers().
>>
>> In case a backend doesn't support non-blocking writes,
>> qemu_chr_write_nb() invokes qemu_chr_write().
>>
>> Individual callers can update their code to add a callback handler,
>> call qemu_chr_write_nb() instead of qemu_chr_write() if they wish to
>> receive -EAGAIN notifications.
>>
>> No backend currently supports non-blocking writes.
>>
>> Signed-off-by: Amit Shah<amit.shah@redhat.com>
>>
>> +ssize_t qemu_chr_write_nb(CharDriverState *s, const uint8_t *buf, size_t len)
>> +{
>> +    if (!s->nonblock) {
>> +        /* Fallback to blocking write if no callback registered */
>> +        return qemu_chr_write(s, buf, len);
>> +    }
>> +
>> +    return s->chr_write_nb(s, buf, len);
>> +}
>>    
>
> I really dislike the idea of adding another function for this.

This suggestion came from Gerd to separate out a write() that blocks and
a write_nb() that doesn't block on -EAGAIN.

>  Can you  
> explain why you need this functionality for virtio-console and why this  
> functionality isn't needed for everything else?

I don't know about everything else; but for virtio-console, a fast guest
could swamp a host chardev with data and while the host chardev blocks
to flush out all the data, a notification to ask the guest to stop is
needed so that we don't lose any data.

(virtio-console asks virtio-serial to throttle any more data, and
virtio-serial doesn't send any more data to this port till it signals
otherwise. The guest, in the meantime, keeps filling its queue till the
queue is full. Once that happens, writes in guests return with -EAGAIN.)

		Amit
Paul Brook - May 5, 2010, 1:34 p.m.
> I really dislike the idea of adding another function for this.  Can you
> explain why you need this functionality for virtio-console and why this
> functionality isn't needed for everything else?

This functionality should (in principle) be used by all serial port 
implementations.

Physical serial ports are sufficiently crufty and low-performance that noone 
actually uses them nowadays.  I expect that the only significant real-world 
use is for serial consoles, which never send enough data to care that writes 
stall the whole machine.

With virtio-serial we've made serial ports a viable solution to a whole range 
of problems.  It's likely that applications that may send nontrivial amounts 
of data, or clients will not be ready to process the data immediately.

Paul
Anthony Liguori - May 5, 2010, 1:53 p.m.
On 05/05/2010 08:34 AM, Paul Brook wrote:
>> I really dislike the idea of adding another function for this.  Can you
>> explain why you need this functionality for virtio-console and why this
>> functionality isn't needed for everything else?
>>      
> This functionality should (in principle) be used by all serial port
> implementations.
>
> Physical serial ports are sufficiently crufty and low-performance that noone
> actually uses them nowadays.  I expect that the only significant real-world
> use is for serial consoles, which never send enough data to care that writes
> stall the whole machine.
>    

We don't implement control flow in the character driver layer today.  
Different backends use different policies.  Some drop data (like pty) 
while other block (like tcp).

This patch adds optional control flow in a pretty crufty way to *some* 
backends but not all.  This just adds a bunch of complexity that will 
certainly introduce bugs.

If we're going to make the char drivers implement control flow, then I 
think we should do it universally--not as an optional feature.  For 
devices that can't participate in control flow, we should decide where 
the policy should be implemented (front-end or back-end) and in either 
approach, it's easy enough to make dropping data or blocking a choice.

Regards,

Anthony Liguori

> With virtio-serial we've made serial ports a viable solution to a whole range
> of problems.  It's likely that applications that may send nontrivial amounts
> of data, or clients will not be ready to process the data immediately.
>
> Paul
>
Paul Brook - May 5, 2010, 2:10 p.m.
> On 05/05/2010 08:34 AM, Paul Brook wrote:
> >> I really dislike the idea of adding another function for this.  Can you
> >> explain why you need this functionality for virtio-console and why this
> >> functionality isn't needed for everything else?
> > 
> > This functionality should (in principle) be used by all serial port
> > implementations.
> > 
> > Physical serial ports are sufficiently crufty and low-performance that
> > noone actually uses them nowadays.  I expect that the only significant
> > real-world use is for serial consoles, which never send enough data to
> > care that writes stall the whole machine.
> 
> We don't implement control flow in the character driver layer today.
> Different backends use different policies.  Some drop data (like pty)
> while other block (like tcp).

Really? I thought data was only dropped when no client was connected, and that 
there was a user visible option to control this.  Either way, I agree that 
this should be done consistently.
 
> This patch adds optional control flow in a pretty crufty way to *some*
> backends but not all.  This just adds a bunch of complexity that will
> certainly introduce bugs.

I admit I've only really looked at the device emulation side of the interface, 
not the chardev backend implementation.

Paul
Gerd Hoffmann - May 5, 2010, 6:40 p.m.
>> + return s->chr_write_nb(s, buf, len);
>> +}
>
> I really dislike the idea of adding another function for this.

Having a explicit function for non-blocking mode IMHO is much better. 
It makes things more clear when reading the code.

Previous approach was to check for O_NONBLOCK (extra syscall) and behave 
differently depending on whenever it is set or not (too much hidden magic).

> Can you
> explain why you need this functionality for virtio-console and why this
> functionality isn't needed for everything else?

virtio-console is just the first user.  16550 emulation can use that 
too.  With the blocking interface you'll stall the guest (sitting in the 
blocking write syscall) in case the outgoing pipe is full.  With the 
non-blocking interface you can simply delay the "you can send more data" 
signal (update status register + raise IRQ) until you can actually 
accept more data.  Meanwhile the guest can continue to run and do 
something else.  Basically it allows to implement sane flow control.

cheers,
   Gerd
Gerd Hoffmann - May 5, 2010, 6:43 p.m.
On 05/05/10 15:53, Anthony Liguori wrote:
> This patch adds optional control flow in a pretty crufty way to *some*
> backends but not all. This just adds a bunch of complexity that will
> certainly introduce bugs.

Amit plans to add support to the others as well.  Beside that there is a 
clearly defined backup plan:  In case the non-blocking interface isn't 
supported by $chardev it will fallback to use the blocking mode.

cheers,
   Gerd
Anthony Liguori - May 5, 2010, 6:48 p.m.
On 05/05/2010 01:40 PM, Gerd Hoffmann wrote:
>>> + return s->chr_write_nb(s, buf, len);
>>> +}
>>
>> I really dislike the idea of adding another function for this.
>
> Having a explicit function for non-blocking mode IMHO is much better. 
> It makes things more clear when reading the code.
>
> Previous approach was to check for O_NONBLOCK (extra syscall) and 
> behave differently depending on whenever it is set or not (too much 
> hidden magic).
>
>> Can you
>> explain why you need this functionality for virtio-console and why this
>> functionality isn't needed for everything else?
>
> virtio-console is just the first user.  16550 emulation can use that 
> too.  With the blocking interface you'll stall the guest (sitting in 
> the blocking write syscall) in case the outgoing pipe is full.  With 
> the non-blocking interface you can simply delay the "you can send more 
> data" signal (update status register + raise IRQ) until you can 
> actually accept more data.  Meanwhile the guest can continue to run 
> and do something else.  Basically it allows to implement sane flow 
> control.

The use of char drivers is dominated by a small number of use cases 
which include serial ports, the monitor, and QMP.

In all of those cases, the devices can and should participate in control 
flow.  IMHO, we should make control flow part of the standard interface 
and fix all users appropriately.

I've seen blocking result in frozen guests many times in real 
deployments.  It's never the right thing to do as far as I can tell.

Regards,

Anthony Liguori

> cheers,
>   Gerd
>
Anthony Liguori - May 5, 2010, 6:49 p.m.
On 05/05/2010 01:43 PM, Gerd Hoffmann wrote:
> On 05/05/10 15:53, Anthony Liguori wrote:
>> This patch adds optional control flow in a pretty crufty way to *some*
>> backends but not all. This just adds a bunch of complexity that will
>> certainly introduce bugs.
>
> Amit plans to add support to the others as well.  Beside that there is 
> a clearly defined backup plan:  In case the non-blocking interface 
> isn't supported by $chardev it will fallback to use the blocking mode.

If we have a second interface, we'll have two interfaces forever.  I'd 
rather see us aggressive remove the blocking interface instead of 
introducing a second interface.

Regards,

Anthony Liguori

> cheers,
>   Gerd
>
Gerd Hoffmann - May 5, 2010, 7:16 p.m.
On 05/05/10 20:49, Anthony Liguori wrote:
> On 05/05/2010 01:43 PM, Gerd Hoffmann wrote:
>> On 05/05/10 15:53, Anthony Liguori wrote:
>>> This patch adds optional control flow in a pretty crufty way to *some*
>>> backends but not all. This just adds a bunch of complexity that will
>>> certainly introduce bugs.
>>
>> Amit plans to add support to the others as well. Beside that there is
>> a clearly defined backup plan: In case the non-blocking interface
>> isn't supported by $chardev it will fallback to use the blocking mode.
>
> If we have a second interface, we'll have two interfaces forever. I'd
> rather see us aggressive remove the blocking interface instead of
> introducing a second interface.

I'm all for killing the blocking interface.  Problem is that converting 
over all users isn't exactly trivial and we have plenty of them.  IMHO 
it isn't realistic to do the switch with a single patch series.

cheers,
   Gerd
Anthony Liguori - May 5, 2010, 7:33 p.m.
On 05/05/2010 02:16 PM, Gerd Hoffmann wrote:
> On 05/05/10 20:49, Anthony Liguori wrote:
>> On 05/05/2010 01:43 PM, Gerd Hoffmann wrote:
>>> On 05/05/10 15:53, Anthony Liguori wrote:
>>>> This patch adds optional control flow in a pretty crufty way to *some*
>>>> backends but not all. This just adds a bunch of complexity that will
>>>> certainly introduce bugs.
>>>
>>> Amit plans to add support to the others as well. Beside that there is
>>> a clearly defined backup plan: In case the non-blocking interface
>>> isn't supported by $chardev it will fallback to use the blocking mode.
>>
>> If we have a second interface, we'll have two interfaces forever. I'd
>> rather see us aggressive remove the blocking interface instead of
>> introducing a second interface.
>
> I'm all for killing the blocking interface.  Problem is that 
> converting over all users isn't exactly trivial and we have plenty of 
> them.  IMHO it isn't realistic to do the switch with a single patch 
> series.

If we're agreed we ought to kill the blocking interface, let's define a 
new proper interface, rename all previous users to something ugly and 
deprecated, and approach it that way.

Let's make it clear what interfaces are supported and which interfaces 
are scheduled to be removed.

Regards,

Anthony Liguori

> cheers,
>   Gerd
>
Gerd Hoffmann - May 6, 2010, 7:11 a.m.
>> I'm all for killing the blocking interface. Problem is that converting
>> over all users isn't exactly trivial and we have plenty of them. IMHO
>> it isn't realistic to do the switch with a single patch series.
>
> If we're agreed we ought to kill the blocking interface, let's define a
> new proper interface, rename all previous users to something ugly and
> deprecated, and approach it that way.

__attribute__ ((deprecated)) ?

cheers,
   Gerd

Patch

diff --git a/net/socket.c b/net/socket.c
index 1c4e153..8a401e6 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(s->fd, (const uint8_t *)&len, sizeof(len), false);
+    return send_all(s->fd, buf, size, false);
 }
 
 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 decf687..5e4dec3 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -145,6 +145,16 @@  int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len)
     return s->chr_write(s, buf, len);
 }
 
+ssize_t qemu_chr_write_nb(CharDriverState *s, const uint8_t *buf, size_t len)
+{
+    if (!s->nonblock) {
+        /* Fallback to blocking write if no callback registered */
+        return qemu_chr_write(s, buf, len);
+    }
+
+    return s->chr_write_nb(s, buf, len);
+}
+
 int qemu_chr_ioctl(CharDriverState *s, int cmd, void *arg)
 {
     if (!s->chr_ioctl)
@@ -203,11 +213,15 @@  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);
 
+    /* We'll set this at connect-time */
+    s->nonblock = false;
+
     /* We're connecting to an already opened device, so let's make sure we
        also get the open event */
     if (s->opened) {
@@ -499,7 +513,7 @@  static CharDriverState *qemu_chr_open_mux(CharDriverState *drv)
 
 
 #ifdef _WIN32
-int send_all(int fd, const void *buf, int len1)
+int send_all(int fd, const void *buf, int len1, bool nonblock)
 {
     int ret, len;
 
@@ -526,7 +540,7 @@  int send_all(int fd, const void *buf, int len1)
 
 #else
 
-static int unix_write(int fd, const uint8_t *buf, int len1)
+static int unix_write(int fd, const uint8_t *buf, int len1, bool nonblock)
 {
     int ret, len;
 
@@ -540,6 +554,9 @@  static int unix_write(int fd, const uint8_t *buf, int len1)
             if (len1 - len) {
                 return len1 - len;
             }
+            if (errno == EAGAIN && nonblock) {
+                return -EAGAIN;
+            }
             if (errno != EAGAIN) {
                 return -1;
             }
@@ -553,9 +570,9 @@  static int unix_write(int fd, const uint8_t *buf, int len1)
     return len1 - len;
 }
 
-int send_all(int fd, const void *buf, int len1)
+int send_all(int fd, const void *buf, int len1, bool nonblock)
 {
-    return unix_write(fd, buf, len1);
+    return unix_write(fd, buf, len1, nonblock);
 }
 #endif /* !_WIN32 */
 
@@ -572,7 +589,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(s->fd_out, buf, len, false);
 }
 
 static int fd_chr_read_poll(void *opaque)
@@ -884,7 +901,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(s->fd, buf, len, false);
 }
 
 static int pty_chr_read_poll(void *opaque)
@@ -1949,7 +1966,7 @@  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);
+        return send_all(s->fd, buf, len, false);
     } else {
         /* XXX: indicate an error ? */
         return len;
diff --git a/qemu-char.h b/qemu-char.h
index eacb3b9..52f9ef1 100644
--- a/qemu-char.h
+++ b/qemu-char.h
@@ -1,6 +1,8 @@ 
 #ifndef QEMU_CHAR_H
 #define QEMU_CHAR_H
 
+#include <stdbool.h>
+
 #include "qemu-common.h"
 #include "qemu-queue.h"
 #include "qemu-option.h"
@@ -53,12 +55,15 @@  typedef void IOEventHandler(void *opaque, int event);
 struct CharDriverState {
     void (*init)(struct CharDriverState *s);
     int (*chr_write)(struct CharDriverState *s, const uint8_t *buf, int len);
+    ssize_t (*chr_write_nb)(struct CharDriverState *s, const uint8_t *buf,
+                            size_t len);
     void (*chr_update_read_handler)(struct CharDriverState *s);
     int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg);
     int (*get_msgfd)(struct CharDriverState *s);
     IOEventHandler *chr_event;
     IOCanReadHandler *chr_can_read;
     IOReadHandler *chr_read;
+    IOHandler *chr_write_unblocked;
     void *handler_opaque;
     void (*chr_send_event)(struct CharDriverState *chr, int event);
     void (*chr_close)(struct CharDriverState *chr);
@@ -68,6 +73,8 @@  struct CharDriverState {
     char *label;
     char *filename;
     int opened;
+    bool nonblock;
+    bool write_blocked;
     QTAILQ_ENTRY(CharDriverState) next;
 };
 
@@ -85,6 +92,7 @@  CharDriverState *qemu_chr_open(const char *label, const char *filename, void (*i
 void qemu_chr_close(CharDriverState *chr);
 void qemu_chr_printf(CharDriverState *s, const char *fmt, ...);
 int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len);
+ssize_t qemu_chr_write_nb(CharDriverState *s, const uint8_t *buf, size_t len);
 void qemu_chr_send_event(CharDriverState *s, int event);
 void qemu_chr_add_handlers(CharDriverState *s, const QemuChrHandlers *handlers,
                            void *opaque);
diff --git a/qemu_socket.h b/qemu_socket.h
index 164ae3e..bdf878b 100644
--- a/qemu_socket.h
+++ b/qemu_socket.h
@@ -23,6 +23,7 @@  int inet_aton(const char *cp, struct in_addr *ia);
 #include <arpa/inet.h>
 #include <netdb.h>
 #include <sys/un.h>
+#include <stdbool.h>
 
 #define socket_error() errno
 #define closesocket(s) close(s)
@@ -35,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(int fd, const void *buf, int len1, bool nonblock);
 
 /* New, ipv6-ready socket helper functions, see qemu-sockets.c */
 int inet_listen_opts(QemuOpts *opts, int port_offset);