FIXME: re-bench
lguest: try to batch interrupts on network receive

Rather than triggering an interrupt every time, we only trigger an
interrupt when there are no more incoming packets (or the recv queue
is full).

However, the overhead of doing the select is measurable.  I tested various
timeouts, including reducing it as the number of pending packets increased,
timing a 1 gigabyte TCP send from Guest -> Host and Host -> Guest:

// time tcpblast -o -s 65536 -c 16k 192.168.2.1:9999 > /dev/null

Timeout		Guest->Host	Pkts/irq	Host->Guest	Pkts/irq
Before		11.3s		1.0		6.3s		1.0
0		11.7s		1.0		6.6s		23.5
1		17.1s		8.8		8.6s		26.0
1/pending	13.4s		1.9		6.6s		23.8
2/pending	13.6s		2.8		6.6s		24.1
5/pending	14.1s		5.0		6.6s		24.4

Note that of the 749818 packets transmitted, 107252 of these times
the Guest has disabled interrupts on the virtqueue (ie. it's polling),
and only 22047 interrupts actually get delivered (ie. 3%).  The Guest
is spending almost all its time with interrupts disabled.
---
 Documentation/lguest/lguest.c |   17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/Documentation/lguest/lguest.c b/Documentation/lguest/lguest.c
--- a/Documentation/lguest/lguest.c
+++ b/Documentation/lguest/lguest.c
@@ -783,6 +783,16 @@ static void net_output(struct virtqueue 
 	add_used(vq, head, len);
 }
 
+/* Will reading from this file descriptor block? */
+static bool will_block(int fd)
+{
+	fd_set fdset;
+	struct timeval zero = { 0, 0 };
+	FD_ZERO(&fdset);
+	FD_SET(fd, &fdset);
+	return select(fd+1, &fdset, NULL, NULL, &zero) != 1;
+}
+
 /* This is where we handle packets coming in from the tun device to our
  * Guest. */
 static void net_input(struct virtqueue *vq)
@@ -795,10 +805,15 @@ static void net_input(struct virtqueue *
 	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->tunfd))
+		trigger_irq(vq);
+
 	len = readv(net_info->tunfd, iov, in);
 	if (len <= 0)
 		err(1, "Failed to read from tun.");
-	add_used_and_trigger(vq, head, len);
+	add_used(vq, head, len);
 }
 
 /* This is the helper to create threads. */
