98 lines
3.1 KiB
Diff
98 lines
3.1 KiB
Diff
|
From 52d73aa49799848042c09be1c64c1bff2159a5e1 Mon Sep 17 00:00:00 2001
|
||
|
From: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
|
||
|
Date: Mon, 20 Aug 2012 10:14:35 +0100
|
||
|
Subject: [PATCH] net: EAGAIN handling for net/socket.c TCP
|
||
|
|
||
|
Replace spinning send_all() with a proper non-blocking send. When the
|
||
|
socket write buffer limit is reached, we should stop trying to send and
|
||
|
wait for the socket to become writable again.
|
||
|
|
||
|
Non-blocking TCP sockets can return in two different ways when the write
|
||
|
buffer limit is reached:
|
||
|
|
||
|
1. ret = -1 and errno = EAGAIN/EWOULDBLOCK. No data has been written.
|
||
|
|
||
|
2. ret < total_size. Short write, only part of the message was
|
||
|
transmitted.
|
||
|
|
||
|
Handle both cases and keep track of how many bytes have been written in
|
||
|
s->send_index. (This includes the 'length' header before the actual
|
||
|
payload buffer.)
|
||
|
|
||
|
Signed-off-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
|
||
|
(cherry picked from commit 45a7f54a8bb3928ffa58d522e0d61acaee8277bb)
|
||
|
|
||
|
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
|
||
|
---
|
||
|
net/socket.c | 36 +++++++++++++++++++++++++++++++-----
|
||
|
1 file changed, 31 insertions(+), 5 deletions(-)
|
||
|
|
||
|
diff --git a/net/socket.c b/net/socket.c
|
||
|
index e5e4e8d..c3e55b8 100644
|
||
|
--- a/net/socket.c
|
||
|
+++ b/net/socket.c
|
||
|
@@ -32,6 +32,7 @@
|
||
|
#include "qemu-error.h"
|
||
|
#include "qemu-option.h"
|
||
|
#include "qemu_socket.h"
|
||
|
+#include "iov.h"
|
||
|
|
||
|
typedef struct NetSocketState {
|
||
|
NetClientState nc;
|
||
|
@@ -40,6 +41,7 @@ typedef struct NetSocketState {
|
||
|
int state; /* 0 = getting length, 1 = getting data */
|
||
|
unsigned int index;
|
||
|
unsigned int packet_len;
|
||
|
+ unsigned int send_index; /* number of bytes sent (only SOCK_STREAM) */
|
||
|
uint8_t buf[4096];
|
||
|
struct sockaddr_in dgram_dst; /* contains inet host and port destination iff connectionless (SOCK_DGRAM) */
|
||
|
IOHandler *send_fn; /* differs between SOCK_STREAM/SOCK_DGRAM */
|
||
|
@@ -88,15 +90,39 @@ static void net_socket_writable(void *opaque)
|
||
|
qemu_flush_queued_packets(&s->nc);
|
||
|
}
|
||
|
|
||
|
-/* XXX: we consider we can send the whole packet without blocking */
|
||
|
static ssize_t net_socket_receive(NetClientState *nc, const uint8_t *buf, size_t size)
|
||
|
{
|
||
|
NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc);
|
||
|
- uint32_t len;
|
||
|
- len = htonl(size);
|
||
|
+ uint32_t len = htonl(size);
|
||
|
+ struct iovec iov[] = {
|
||
|
+ {
|
||
|
+ .iov_base = &len,
|
||
|
+ .iov_len = sizeof(len),
|
||
|
+ }, {
|
||
|
+ .iov_base = (void *)buf,
|
||
|
+ .iov_len = size,
|
||
|
+ },
|
||
|
+ };
|
||
|
+ size_t remaining;
|
||
|
+ ssize_t ret;
|
||
|
+
|
||
|
+ remaining = iov_size(iov, 2) - s->send_index;
|
||
|
+ ret = iov_send(s->fd, iov, 2, s->send_index, remaining);
|
||
|
|
||
|
- send_all(s->fd, (const uint8_t *)&len, sizeof(len));
|
||
|
- return send_all(s->fd, buf, size);
|
||
|
+ if (ret == -1 && errno == EAGAIN) {
|
||
|
+ ret = 0; /* handled further down */
|
||
|
+ }
|
||
|
+ if (ret == -1) {
|
||
|
+ s->send_index = 0;
|
||
|
+ return -errno;
|
||
|
+ }
|
||
|
+ if (ret < (ssize_t)remaining) {
|
||
|
+ s->send_index += ret;
|
||
|
+ net_socket_write_poll(s, true);
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+ s->send_index = 0;
|
||
|
+ return size;
|
||
|
}
|
||
|
|
||
|
static ssize_t net_socket_receive_dgram(NetClientState *nc, const uint8_t *buf, size_t size)
|
||
|
--
|
||
|
1.7.12.1
|
||
|
|