---
 drivers/char/vring.c  |    9 ++++++---
 drivers/net/tun.c     |   22 +++++++++++++++++-----
 include/linux/vring.h |    2 +-
 3 files changed, 24 insertions(+), 9 deletions(-)

diff -r 8e4153543db1 drivers/char/vring.c
--- a/drivers/char/vring.c	Fri Apr 25 08:50:48 2008 +1000
+++ b/drivers/char/vring.c	Thu May 01 15:20:04 2008 +1000
@@ -304,8 +304,10 @@ EXPORT_SYMBOL_GPL(vring_get_buffer);
  * @vr: the vring
  * @id: the id returned from vring_get_buffer
  * @len: the total bytes *written* to the buffer
+ *
+ * Returns 0 or a negative errno.
  */
-void vring_used_buffer(struct vring_info *vr, int id, u32 len)
+int vring_used_buffer(struct vring_info *vr, int id, u32 len)
 {
 	struct vring_used_elem used;
 	u16 used_idx;
@@ -315,15 +317,16 @@ void vring_used_buffer(struct vring_info
 	used.id = id - 1;
 	used.len = len;
 	if (get_user(used_idx, &vr->ring.used->idx) != 0)
-		return;
+		return -EFAULT;
 
 	if (copy_to_user(&vr->ring.used->ring[used_idx & vr->mask], &used,
 			 sizeof(used)))
-		return;
+		return -EFAULT;
 
 	wmb();
 	used_idx++;
 	put_user(used_idx, &vr->ring.used->idx);
+	return 0;
 }
 EXPORT_SYMBOL_GPL(vring_used_buffer);
 
diff -r 8e4153543db1 drivers/net/tun.c
--- a/drivers/net/tun.c	Fri Apr 25 08:50:48 2008 +1000
+++ b/drivers/net/tun.c	Thu May 01 15:20:04 2008 +1000
@@ -529,7 +529,12 @@ static int pull_recv_skbs(void *_tun)
 			break;
 		}
 
-		vring_used_buffer(tun->inring, id, sizeof(gso) + skb->len);
+		err = vring_used_buffer(tun->inring, id, sizeof(gso)+skb->len);
+		if (unlikely(err)) {
+			tun->dev->stats.tx_fifo_errors++;
+			break;
+		}
+
 		num_copied++;
 	}
 
@@ -561,6 +566,7 @@ static int pull_finished_buffers(void *_
 {
 	struct tun_struct *tun = _tun;
 	struct skb_tun_hdr *i;
+	int ret = 0, err;
 	LIST_HEAD(list);
 
 	spin_lock_irq(&tun->outring_lock);
@@ -573,7 +579,9 @@ static int pull_finished_buffers(void *_
 		list_del(&i->list);
 
 		/* This is a xmit packet, so we wrote 0 bytes to it. */
-		vring_used_buffer(tun->outring, i->id, 0);
+		err = vring_used_buffer(tun->outring, i->id, 0);
+		if (err)
+			ret = err;
 
 		/* Release device.  Keeping this reference blocks file close. */
 		dev_put(tun->dev);
@@ -581,7 +589,7 @@ static int pull_finished_buffers(void *_
 		/* i == skb->head. */
 		kfree(i);
 	}
-	return 0;
+	return ret;
 }
 
 /* We are done with this skb data: put it in the used pile. */
@@ -642,8 +650,12 @@ static int xmit_packets(void *_tun)
 			dev_hold(tun->dev);
 			skb_shinfo(skb)->destructor = shinfo_finished;
 		} else {
-			/* Queue immediately for pull_finished_buffers. */
-			vring_used_buffer(tun->outring, id, 0);
+			/* We're done already. */
+			err = vring_used_buffer(tun->outring, id, 0);
+			if (err) {
+				kfree_skb(skb);
+				return err;
+			}
 			wake = 1;
 		}
 		netif_rx_ni(skb);
diff -r 8e4153543db1 include/linux/vring.h
--- a/include/linux/vring.h	Fri Apr 25 08:50:48 2008 +1000
+++ b/include/linux/vring.h	Thu May 01 15:20:04 2008 +1000
@@ -59,7 +59,7 @@ int vring_get_buffer(struct vring_info *
 		     struct iovec *out_iov,
 		     unsigned int num_out, unsigned long *out_len);
 
-void vring_used_buffer(struct vring_info *vr, int id, u32 len);
+int vring_used_buffer(struct vring_info *vr, int id, u32 len);
 
 void vring_wake(struct vring_info *vr);
 #endif /* __KERNEL__ */
