virtio: add debugging if driver doesn't kick.
Rusty Russell [Thu, 12 Jan 2012 05:14:43 +0000 (15:14 +1030)]
Under the existing #ifdef DEBUG, check that they don't have more than
1/10 of a second between an add_buf() and a
virtqueue_notify()/virtqueue_kick_prepare() call.

We could get false positives on a really busy system, but good for
development.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>

drivers/virtio/virtio_ring.c

index 36bb6a6..79e1b29 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/device.h>
 #include <linux/slab.h>
 #include <linux/module.h>
+#include <linux/hrtimer.h>
 
 /* virtio guest is communicating with a virtual "device" that actually runs on
  * a host processor.  Memory barriers are used to control SMP effects. */
@@ -108,6 +109,10 @@ struct vring_virtqueue
 #ifdef DEBUG
        /* They're supposed to lock for us. */
        unsigned int in_use;
+
+       /* Figure out if their kicks are too delayed. */
+       bool last_add_time_valid;
+       ktime_t last_add_time;
 #endif
 
        /* Tokens for callbacks. */
@@ -198,6 +203,19 @@ int virtqueue_add_buf(struct virtqueue *_vq,
 
        BUG_ON(data == NULL);
 
+#ifdef DEBUG
+       {
+               ktime_t now = ktime_get();
+
+               /* No kick or get, with .1 second between?  Warn. */
+               if (vq->last_add_time_valid)
+                       WARN_ON(ktime_to_ms(ktime_sub(now, vq->last_add_time))
+                                           > 100);
+               vq->last_add_time = now;
+               vq->last_add_time_valid = true;
+       }
+#endif
+
        /* If the host supports indirect descriptor tables, and we have multiple
         * buffers, then go indirect. FIXME: tune this threshold */
        if (vq->indirect && (out + in) > 1 && vq->num_free) {
@@ -298,6 +316,14 @@ bool virtqueue_kick_prepare(struct virtqueue *_vq)
        new = vq->vring.avail->idx;
        vq->num_added = 0;
 
+#ifdef DEBUG
+       if (vq->last_add_time_valid) {
+               WARN_ON(ktime_to_ms(ktime_sub(ktime_get(),
+                                             vq->last_add_time)) > 100);
+       }
+       vq->last_add_time_valid = false;
+#endif
+
        if (vq->event) {
                needs_kick = vring_need_event(vring_avail_event(&vq->vring),
                                              new, old);
@@ -435,6 +461,10 @@ void *virtqueue_get_buf(struct virtqueue *_vq, unsigned int *len)
                virtio_mb(vq);
        }
 
+#ifdef DEBUG
+       vq->last_add_time_valid = false;
+#endif
+
        END_USE(vq);
        return ret;
 }
@@ -620,6 +650,7 @@ struct virtqueue *vring_new_virtqueue(unsigned int num,
        list_add_tail(&vq->vq.list, &vdev->vqs);
 #ifdef DEBUG
        vq->in_use = false;
+       vq->last_add_time_valid = false;
 #endif
 
        vq->indirect = virtio_has_feature(vdev, VIRTIO_RING_F_INDIRECT_DESC);