Before:
	1Gping-throughput.sh: Seconds: 20
	1Gping-throughput.sh: Packets: 1000001,1000004
	1Gping-throughput.sh: Interrupts: 966239,1
	1Gping-throughput.sh: Notifications: 1089(14275),963192(36813)
	Net IRQs triggered: 966443(12192),1(962475)
	1Mping.sh: Seconds: 35
	1Mping.sh: Packets: 1000001,1000004
	1Mping.sh: Interrupts: 1000001,1
	1Mping.sh: Notifications: 15385(0),1000004(1)
	Net IRQs triggered: 1000001(0),1(1000004)

After:
	1Gping-throughput.sh: Seconds: 23
	1Gping-throughput.sh: Packets: 1000001,1000004
	1Gping-throughput.sh: Interrupts: 999906,1
	1Gping-throughput.sh: Notifications: 15385(0),999966(39)
	Net IRQs triggered: 999932(16),1(999918)
	1Mping.sh: Seconds: 24
	1Mping.sh: Packets: 1000001,1000004
	1Mping.sh: Interrupts: 1000001,1
	1Mping.sh: Notifications: 15385(0),1000003(2)
	Net IRQs triggered: 1000001(5),1(1000003)
---
 Documentation/lguest/lguest.c |  154 +++++++++++++++++++-----------------------
 1 file changed, 71 insertions(+), 83 deletions(-)

diff --git a/Documentation/lguest/lguest.c b/Documentation/lguest/lguest.c
--- a/Documentation/lguest/lguest.c
+++ b/Documentation/lguest/lguest.c
@@ -799,8 +799,6 @@ static void console_output(struct virtqu
  * kernel network stack.
  */
 struct net_info {
-	/* We write packets to the pipe. */
-	int p[2];
 	/* Our hardware address */
 	unsigned char ethaddr[ETH_ALEN];
 	unsigned char ipaddr[4];
@@ -887,45 +885,69 @@ static void csum_replace(__u16 *sum, u32
 	*sum = csum_fold(csum_partial(diff, sizeof(diff), *sum ^ 0xFFFF));
 }
 
-static unsigned int copy_iov(char *buffer,
-			     const struct iovec *iov, unsigned iovnum,
-			     unsigned buflen)
+static unsigned int copy_iov(struct iovec *dstiov, unsigned dstnum,
+			     const struct iovec *srciov, unsigned srcnum)
 {
-	unsigned int len = 0;
+	unsigned int dstoff = 0, srcoff = 0;
+	unsigned int totlen = 0;
 
-	while (iovnum) {
-		if (len + iov->iov_len > buflen)
-			err(1, "iovec too long");
-		memcpy(buffer + len, iov->iov_base, iov->iov_len);
-		len += iov->iov_len;
-		iovnum--;
-		iov++;
+	while (srcnum) {
+		unsigned int len;
+
+		if (dstiov->iov_len - dstoff > srciov->iov_len - srcoff)
+			len = srciov->iov_len - srcoff;
+		else
+			len = dstiov->iov_len - dstoff;
+
+		memcpy(dstiov->iov_base + dstoff, srciov->iov_base + srcoff,
+		       len);
+		dstoff += len;
+		srcoff += len;
+		totlen += len;
+		if (dstoff == dstiov->iov_len) {
+			dstiov++;
+			dstnum--;
+			if (dstnum == 0)
+				errx(1, "dst buf too short (%u)", totlen);
+			dstoff = 0;
+		}
+		if (srcoff == srciov->iov_len) {
+			srciov++;
+			srcnum--;
+			srcoff = 0;
+		}
 	}
-
-	return len;
+	return totlen;
 }
 
-static void net_output(struct virtqueue *vq)
+static void do_net_output(struct net_info *net_info,
+			  struct virtqueue *outvq, struct virtqueue *invq)
 {
-	struct net_info *net_info = vq->dev->priv;
-	unsigned int head, out, in;
-	struct iovec iov[vq->vring.num];
+	unsigned int out_head, out, in, in_head, dummy;
+	struct iovec outiov[outvq->vring.num], iniov[invq->vring.num];
 	struct ether_header *ehdr;
-	char pkt[2 + sizeof(struct virtio_net_hdr) + 1514];
 	unsigned int len;
 
-	vq->irq = &xmit_irq;
-	vq->irq_suppressed = &xmit_irq_suppressed;
+	in_head = wait_for_vq_desc(invq, iniov, &dummy, &in);
+	if (dummy)
+		errx(1, "Output buffers in net input queue?");
 
-	head = wait_for_vq_desc(vq, iov, &out, &in);
-	if (in)
+	/* If we're going to block, make sure we trigger receive first. */
+	if (lg_last_avail(outvq) == outvq->vring.avail->idx)
+		trigger_irq(invq);
+
+	out_head = wait_for_vq_desc(outvq, outiov, &out, &dummy);
+	if (dummy)
 		errx(1, "Input buffers in net output queue?");
 
-	len = copy_iov(pkt+2, iov, out, sizeof(pkt));
+	/* We transfer and operate on packet inside guest.  This only works
+	 * so well because guest currently uses v. simple buffer layout. */
+	len = copy_iov(iniov, in, outiov, out);
+
 	if (len < sizeof(struct virtio_net_hdr) + 14)
 		errx(1, "Short packet %u", len);
 
-	ehdr = (struct ether_header *)(pkt + 2 + sizeof(struct virtio_net_hdr));
+	ehdr = iniov[1].iov_base;
 	len -= sizeof(struct virtio_net_hdr);
 	if (ehdr->ether_type == htons(ETHERTYPE_ARP)) {
 		struct arphdr *arp = (struct arphdr *)(ehdr + 1);
@@ -973,67 +995,36 @@ static void net_output(struct virtqueue 
 	/* Copy our ethaddr to source ethaddr. */
 	memcpy(ehdr->ether_shost, net_info->ethaddr, ETH_ALEN);
 
-	/* We put the lenght first, to packetize. */
-	*(u16 *)pkt = len + sizeof(struct virtio_net_hdr);
-	if (write(net_info->p[1], pkt, 2 + sizeof(struct virtio_net_hdr) + len)
-	    != 2 + sizeof(struct virtio_net_hdr) + len)
-		err(1, "Writing to pipe");
+	add_used(invq, in_head, len+sizeof(struct virtio_net_hdr));
 consume:
-	add_used(vq, head, len+sizeof(struct virtio_net_hdr));
+	add_used(outvq, out_head, len+sizeof(struct virtio_net_hdr));
 }
 
-/* Will reading from this file descriptor block? */
-static bool will_block(int fd)
+static void net_output(struct virtqueue *outvq)
 {
-	fd_set fdset;
-	struct timeval zero = { 0, 0 };
-	FD_ZERO(&fdset);
-	FD_SET(fd, &fdset);
-	return select(fd+1, &fdset, NULL, NULL, &zero) != 1;
-}
+	struct net_info *net_info = outvq->dev->priv;
+	struct virtqueue *invq = outvq->dev->vq;
+	unsigned long args[] = { LHREQ_EVENTFD,
+				 invq->config.pfn*getpagesize(), 0 };
 
-static void trim_iov(struct iovec *iov, unsigned num_iov, unsigned len)
-{
-	unsigned int i;
+	/* Create a zero-initialized eventfd. */
+	invq->eventfd = eventfd(0, 0);
+	if (invq->eventfd < 0)
+		err(1, "Creating eventfd");
+	args[2] = invq->eventfd;
 
-	for (i = 0; i < num_iov; i++) {
-		if (iov[i].iov_len > len)
-			iov[i].iov_len = len;
-		len -= iov[i].iov_len;
-	}
-}
+	/* Attach an eventfd for input virtqueue. */
+	if (write(lguest_fd, &args, sizeof(args)) != 0)
+		err(1, "Attaching eventfd");
 
-/* This is where we handle packets coming in from the tun device to our
- * Guest. */
-static void net_input(struct virtqueue *vq)
-{
-	int len;
-	unsigned int head, out, in;
-	struct iovec iov[vq->vring.num];
-	u16 pkt_len;
-	struct net_info *net_info = vq->dev->priv;
+	/* Set up stats. */
+	invq->irq = &recv_irq;
+	invq->irq_suppressed = &recv_irq_suppressed;
+	outvq->irq = &xmit_irq;
+	outvq->irq_suppressed = &xmit_irq_suppressed;
 
-	vq->irq = &recv_irq;
-	vq->irq_suppressed = &recv_irq_suppressed;
-
-	head = wait_for_vq_desc(vq, iov, &out, &in);
-	if (out)
-		errx(1, "Output buffers in net input queue?");
-
-	/* Deliver interrupt now, since we're about to sleep. */
-	if (vq->pending_used && will_block(net_info->p[0]))
-		trigger_irq(vq);
-
-	if (read(net_info->p[0], &pkt_len, 2) != 2)
-		err(1, "Failed to read length from pipe.");
-
-	/* Don't read in more than the one packet! */
-	trim_iov(iov, in, pkt_len);
-
-	len = readv(net_info->p[0], iov, in);
-	if (len != pkt_len)
-		err(1, "Failed to read from pipe: %u vs %u", len, pkt_len);
-	add_used(vq, head, len);
+	for (;;)
+		do_net_output(net_info, outvq, invq);
 }
 
 /* This is the helper to create threads. */
@@ -1430,16 +1421,13 @@ static void setup_dummy_net(char *arg)
 	struct virtio_net_config conf;
 	u32 ip;
 
-	if (pipe(net_info->p) != 0)
-		err(1, "Creating net pipe");
-
 	/* First we create a new network device. */
 	dev = new_device("net", VIRTIO_ID_NET);
 	dev->priv = net_info;
 
 	/* Network devices need a receive and a send queue, just like
 	 * console. */
-	add_virtqueue(dev, VIRTQUEUE_NUM, net_input);
+	add_virtqueue(dev, VIRTQUEUE_NUM, NULL);
 	add_virtqueue(dev, VIRTQUEUE_NUM, net_output);
 
 	/* Set IP address. */
