[PATCH 4/4] Stop dropping so many RX packets in tap

Anthony Liguori
2008-05-04T02:47:13Z

Normally, tap always reads packets and simply lets the client drop them if it
cannot receive them. For virtio-net, this results in massive packet loss and
about an 80% performance loss in TCP throughput.

This patch modifies qemu_send_packet() to only deliver a packet to a VLAN
client if it doesn't have a fd_can_read method or the fd_can_read method
indicates that it can receive packets. We also return a status of whether
any clients were able to receive the packet.

If no clients were able to receive a packet, we buffer the packet until a
client indicates that it can receive packets again.

This patch also modifies the tap code to only read from the tap fd if at least
one client on the VLAN is able to receive a packet.

Finally, this patch changes the tap code to drain all possible packets from
the tap device when the tap fd is readable.

Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>

diff --git a/qemu/net.h b/qemu/net.h
index 13daa27..dfdf9af 100644
--- a/qemu/net.h
+++ b/qemu/net.h
@@ -29,7 +29,7 @@ VLANClientState *qemu_new_vlan_client(VLANState *vlan,
IOCanRWHandler *fd_can_read,
void *opaque);
int qemu_can_send_packet(VLANClientState *vc);
-void qemu_send_packet(VLANClientState *vc, const uint8_t *buf, int size);
+int qemu_send_packet(VLANClientState *vc, const uint8_t *buf, int size);
void qemu_handler_true(void *opaque);

void do_info_network(void);
diff --git a/qemu/vl.c b/qemu/vl.c
index b8ce485..74c34b6 100644
--- a/qemu/vl.c
+++ b/qemu/vl.c
@@ -3750,10 +3750,11 @@ int qemu_can_send_packet(VLANClientState *vc1)
return 0;
}

-void qemu_send_packet(VLANClientState *vc1, const uint8_t *buf, int size)
+int qemu_send_packet(VLANClientState *vc1, const uint8_t *buf, int size)
{
VLANState *vlan = vc1->vlan;
VLANClientState *vc;
+ int ret = -EAGAIN;

#if 0
printf("vlan %d send:\n", vlan->id);
@@ -3761,9 +3762,14 @@ void qemu_send_packet(VLANClientState *vc1, const uint8_t *buf, int size)
#endif
for(vc = vlan->first_client; vc != NULL; vc = vc->next) {
if (vc != vc1) {
- vc->fd_read(vc->opaque, buf, size);
+ if (!vc->fd_can_read || vc->fd_can_read(vc->opaque)) {
+ vc->fd_read(vc->opaque, buf, size);
+ ret = 0;
+ }
}
}
+
+ return ret;
}

#if defined(CONFIG_SLIRP)
@@ -3966,6 +3972,8 @@ typedef struct TAPState {
VLANClientState *vc;
int fd;
char down_script[1024];
+ char buf[4096];
+ int size;
} TAPState;

static void tap_receive(void *opaque, const uint8_t *buf, int size)
@@ -3981,24 +3989,70 @@ static void tap_receive(void *opaque, const uint8_t *buf, int size)
}
}

+static int tap_can_send(void *opaque)
+{
+ TAPState *s = opaque;
+ VLANClientState *vc;
+ int can_receive = 0;
+
+ /* Check to see if any of our clients can receive a packet */
+ for (vc = s->vc->vlan->first_client; vc; vc = vc->next) {
+ /* Skip ourselves */
+ if (vc == s->vc)
+ continue;
+
+ if (!vc->fd_can_read) {
+ /* no fd_can_read handler, they always can receive */
+ can_receive = 1;
+ } else
+ can_receive = vc->fd_can_read(vc->opaque);
+
+ /* Once someone can receive, we try to send a packet */
+ if (can_receive)
+ break;
+ }
+
+ return can_receive;
+}
+
static void tap_send(void *opaque)
{
TAPState *s = opaque;
- uint8_t buf[4096];
- int size;

+ /* First try to send any buffered packet */
+ if (s->size > 0) {
+ int err;
+
+ /* If noone can receive the packet, buffer it */
+ err = qemu_send_packet(s->vc, s->buf, s->size);
+ if (err == -EAGAIN)
+ return;
+ }
+
+ /* Read packets until we hit EAGAIN */
+ do {
#ifdef __sun__
- struct strbuf sbuf;
- int f = 0;
- sbuf.maxlen = sizeof(buf);
- sbuf.buf = buf;
- size = getmsg(s->fd, NULL, &sbuf, &f) >=0 ? sbuf.len : -1;
+ struct strbuf sbuf;
+ int f = 0;
+ sbuf.maxlen = sizeof(s->buf);
+ sbuf.buf = s->buf;
+ s->size = getmsg(s->fd, NULL, &sbuf, &f) >=0 ? sbuf.len : -1;
#else
- size = read(s->fd, buf, sizeof(buf));
+ s->size = read(s->fd, s->buf, sizeof(s->buf));
#endif
- if (size > 0) {
- qemu_send_packet(s->vc, buf, size);
- }
+
+ if (s->size == -1 && errno == EINTR)
+ continue;
+
+ if (s->size > 0) {
+ int err;
+
+ /* If noone can receive the packet, buffer it */
+ err = qemu_send_packet(s->vc, s->buf, s->size);
+ if (err == -EAGAIN)
+ break;
+ }
+ } while (s->size > 0);
}

/* fd support */
@@ -4012,7 +4066,7 @@ static TAPState *net_tap_fd_init(VLANState *vlan, int fd)
return NULL;
s->fd = fd;
s->vc = qemu_new_vlan_client(vlan, tap_receive, NULL, s);
- qemu_set_fd_handler2(s->fd, NULL, tap_send, NULL, s);
+ qemu_set_fd_handler2(s->fd, tap_can_send, tap_send, NULL, s);
snprintf(s->vc->info_str, sizeof(s->vc->info_str), "tap: fd=%d", fd);
return s;