virtio: put an explicit threshold in the virtio_ring

Experiment.  Unfinished.

FIXME: Adjust vring_size

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
---
 drivers/virtio/virtio_ring.c |   16 +++++++++++++---
 include/linux/virtio_ring.h  |   35 ++++++++++++++++++++++++++++++++---
 2 files changed, 45 insertions(+), 6 deletions(-)

diff -r 7644742684e9 drivers/virtio/virtio_ring.c
--- a/drivers/virtio/virtio_ring.c	Tue Jul 01 11:28:06 2008 +1000
+++ b/drivers/virtio/virtio_ring.c	Tue Jul 01 14:00:59 2008 +1000
@@ -88,7 +88,8 @@ static int vring_add_buf(struct virtqueu
 		/* FIXME: for historical reasons, we force a notify here if
 		 * there are outgoing parts to the buffer.  Presumably the
 		 * host should service the ring ASAP. */
-		if (out)
+		if (!test_bit(VIRTIO_RING_F_THRESHOLD, vq->vq.vdev->features)
+		    && out)
 			vq->notify(&vq->vq);
 		END_USE(vq);
 		return -ENOSPC;
@@ -134,21 +135,29 @@ static void vring_kick(struct virtqueue 
 static void vring_kick(struct virtqueue *_vq)
 {
 	struct vring_virtqueue *vq = to_vvq(_vq);
+	u16 oldidx;
+
 	START_USE(vq);
 	/* Descriptors and available array need to be set before we expose the
 	 * new available array entries. */
 	wmb();
 
+	oldidx = vq->vring.avail->idx;
 	vq->vring.avail->idx += vq->num_added;
 	vq->num_added = 0;
 
 	/* Need to update avail index before checking if we should notify */
 	mb();
 
-	if (!(vq->vring.used->flags & VRING_USED_F_NO_NOTIFY))
+	if (!(vq->vring.used->flags & VRING_USED_F_NO_NOTIFY)) {
 		/* Prod other side to tell it about changes. */
 		vq->notify(&vq->vq);
-
+	} else if (test_bit(VIRTIO_RING_F_THRESHOLD, vq->vq.vdev->features)) {
+		u16 thresh = vring_avail_threshold(&vq->vring);
+		/* Did we cross threshold?  If so, notify.  Cast avoids wrap. */
+		if (oldidx < thresh && (u32)oldidx + vq->num_added > thresh)
+			vq->notify(&vq->vq);
+	}
 	END_USE(vq);
 }
 
@@ -331,6 +340,7 @@ void vring_transport_features(struct vir
 
 	for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++) {
 		switch (i) {
+		case VIRTIO_RING_F_THRESHOLD:
 		case VIRTIO_RING_F_PUBLISH_INDICES:
 			break;
 		default:
diff -r 7644742684e9 include/linux/virtio_ring.h
--- a/include/linux/virtio_ring.h	Tue Jul 01 11:28:06 2008 +1000
+++ b/include/linux/virtio_ring.h	Tue Jul 01 14:00:59 2008 +1000
@@ -26,6 +26,8 @@
 
 /* We publish our last-seen used index at the end of the avail ring. */
 #define VIRTIO_RING_F_PUBLISH_INDICES	28
+/* Threshold for notifications/interrupts */
+#define VIRTIO_RING_F_THRESHOLD		29
 
 /* Virtio ring descriptors: 16 bytes.  These can chain together via "next". */
 struct vring_desc
@@ -63,6 +65,13 @@ struct vring_used
 	struct vring_used_elem ring[];
 };
 
+struct vring_tailer {
+	/* Consumed count */
+	__u16 last_seen;
+	/* Threshold to notify/interrupt */
+	__u16 threshold;
+};
+
 struct vring {
 	unsigned int num;
 
@@ -87,6 +96,9 @@ struct vring {
  *	__u16 available[num];
  *	__u16 last_used_idx;
  *
+ *	__u16 last_seen_avail;
+ *	__u16 avail_threshold;
+ *
  *	// Padding to the next page boundary.
  *	char pad[];
  *
@@ -95,6 +107,9 @@ struct vring {
  *	__u16 used_idx;
  *	struct vring_used_elem used[num];
  *	__u16 last_avail_idx;
+ *
+ *	__u16 last_seen_used;
+ *	__u16 used_threshold;
  * };
  */
 static inline void vring_init(struct vring *vr, unsigned int num, void *p,
@@ -111,13 +126,27 @@ static inline unsigned vring_size(unsign
 {
 	return ((sizeof(struct vring_desc) * num + sizeof(__u16) * (2 + num)
 		 + pagesize - 1) & ~(pagesize - 1))
-		+ sizeof(__u16) * 2 + sizeof(struct vring_used_elem) * num + 2;
+		+ sizeof(__u16) * 2 + sizeof(struct vring_used_elem) * num
+		+ sizeof(struct vring_tailer);
 }
 
 /* We publish the last-seen used index at the end of the available ring, and
  * vice-versa.  These are at the end for backwards compatibility. */
-#define vring_last_used(vr) ((vr)->avail->ring[(vr)->num])
-#define vring_last_avail(vr) (*(__u16 *)&(vr)->used->ring[(vr)->num])
+static inline struct vring_tailer *vring_avail_tailer(const struct vring *vr)
+{
+	return (struct vring_tailer *)&(vr->used->ring[vr->num]);
+}
+
+static inline struct vring_tailer *vring_used_tailer(const struct vring *vr)
+{
+	return (struct vring_tailer *)&(vr->avail->ring[vr->num]);
+}
+
+#define vring_last_used(vr) (vring_avail_tailer(vr)->last_seen)
+#define vring_last_avail(vr) (vring_used_tailer(vr)->last_seen)
+
+#define vring_avail_threshold(vr) (vring_used_tailer(vr)->threshold)
+#define vring_used_threshold(vr) (vring_avail_tailer(vr)->threshold)
 
 #ifdef __KERNEL__
 #include <linux/irqreturn.h>
