---
 drivers/char/vring.c  |   26 ++++++++++++++++++++++----
 drivers/net/tun.c     |   36 +++++++++---------------------------
 include/linux/vring.h |    3 ++-
 3 files changed, 33 insertions(+), 32 deletions(-)

diff -r c12c66b35764 Documentation/lguest/lguest
Binary file Documentation/lguest/lguest has changed
diff -r c12c66b35764 drivers/char/vring.c
--- a/drivers/char/vring.c	Thu Jul 24 18:31:16 2008 +1000
+++ b/drivers/char/vring.c	Fri Jul 25 10:07:35 2008 +1000
@@ -33,6 +33,8 @@ struct vring_info {
 	u16 mask;
 	u16 last_used;
 
+	bool needs_pulling;
+
 	unsigned long base, limit;
 
 	const struct vring_ops *ops;
@@ -67,6 +69,10 @@ static unsigned int vring_poll(struct fi
 	if (used != vr->last_used)
 		return POLLIN | POLLRDNORM;
 
+	/* Already marked for pulling by a push? */
+	if (vr->needs_pulling)
+		return POLLIN | POLLRDNORM;
+
 	/* If we need to pull, it's also readable. */
 	mutex_lock(&vr->lock);
 	if (vr->ops && vr->ops->can_pull && vr->ops->can_pull(vr->ops_data))
@@ -87,9 +93,10 @@ static ssize_t vring_read(struct file *f
 	/* Some uses of vrings require updating in user context.  This
 	 * is best done close to the caller, ie. here. */
 	mutex_lock(&vr->lock);
-	if (vr->ops && vr->ops->pull)
+	if (vr->ops && vr->ops->pull) {
+		vr->needs_pulling = false;
 		err = vr->ops->pull(vr->ops_data);
-	else
+	} else
 		err = 0;
 	mutex_unlock(&vr->lock);
 
@@ -108,9 +115,20 @@ static ssize_t vring_write(struct file *
 	int err;
 
 	mutex_lock(&vr->lock);
-	if (vr->ops && vr->ops->push)
+	if (vr->ops && vr->ops->push) {
 		err = vr->ops->push(vr->ops_data);
-	else
+		if (err > 0) {
+			int flags;
+			/* Must write to ring *before* checking flags. */
+			mb();
+			flags = vr->ring.avail->flags;
+			if (!(flags & VRING_AVAIL_F_NO_INTERRUPT)) {
+				vr->needs_pulling = true;
+				vring_wake(vr);
+			}
+			err = 0;
+		}
+	} else
 		err = 0;
 	mutex_unlock(&vr->lock);
 
diff -r c12c66b35764 drivers/net/tun.c
--- a/drivers/net/tun.c	Thu Jul 24 18:31:16 2008 +1000
+++ b/drivers/net/tun.c	Fri Jul 25 10:07:35 2008 +1000
@@ -109,7 +109,6 @@ struct tun_struct {
 	/* List for user-mapped skbs successfully transmitted */
 	struct vring_info	*inring, *outring;
 	struct file		*infile, *outfile;
-	bool			outring_progress;
 
 #ifdef TUN_DEBUG
 	int debug;
@@ -627,7 +626,12 @@ static bool pending_recv_skbs(void *_tun
 {
 	struct tun_struct *tun = _tun;
 
-	return !skb_queue_empty(&tun->readq) && vring_has_buffer(tun->inring);
+	if (!vring_has_buffer(tun->inring)) {
+		vring_enable_notify(tun->inring);
+		return false;
+	}		
+
+	return !skb_queue_empty(&tun->readq);
 }
 
 /* Returns 0, or negative errno. */
@@ -682,30 +686,13 @@ static struct vring_ops recvops = {
 	.pull = pull_recv_skbs,
 };
 
-/* Returns whether there are queued buffers */
-static bool finished_xmit_buffers(void *_tun)
-{
-	struct tun_struct *tun = _tun;
-
-	return tun->outring_progress;
-}
-
-/* Returns 0, or negative errno. */
-static int pull_finished_buffers(void *_tun)
-{
-	struct tun_struct *tun = _tun;
-
-	/* Simply clear progress flag. */
-	tun->outring_progress = false;
-	return 0;
-}
-
 static int xmit_packets(void *_tun)
 {
 	struct tun_struct *tun = _tun;
 	struct iovec iov[2+MAX_SKB_FRAGS];
 	int id;
 	unsigned long len;
+	bool progress = false;
 
 	while ((id = vring_get_buffer(tun->outring, NULL, 0, NULL,
 				      iov, ARRAY_SIZE(iov), &len)) > 0) {
@@ -719,19 +706,14 @@ static int xmit_packets(void *_tun)
 		if (unlikely(err))
 			return err;
 
-		tun->outring_progress = true;
+		progress = true;
 	}
 
-	if (tun->outring_progress)
-		vring_wake(tun->outring);
-
-	return 0;
+	return progress;
 }
 
 static struct vring_ops xmitops = {
 	.push = xmit_packets,
-	.can_pull = finished_xmit_buffers,
-	.pull = pull_finished_buffers,
 };
 
 static int set_recv_vring(struct tun_struct *tun, int fd)
diff -r c12c66b35764 include/linux/vring.h
--- a/include/linux/vring.h	Thu Jul 24 18:31:16 2008 +1000
+++ b/include/linux/vring.h	Fri Jul 25 10:07:35 2008 +1000
@@ -40,7 +40,8 @@ struct vring_ops {
 	/* Returns 0 or negative errno. */
 	int (*pull)(void *ops_data);
 
-	/* Returns 0 or negative errno. */
+	/* Returns 0, negative errno or positive to indicate progress
+	 * (ie. stuff to pull). */
 	int (*push)(void *ops_data);
 };
 
