diff mbox

[4/5] libqos: add qvirtqueue_get_buf()

Message ID 1462437137-19824-5-git-send-email-stefanha@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Stefan Hajnoczi May 5, 2016, 8:32 a.m. UTC
Add a function to pop buffers off the used ring.  Up until now the
virtio device tests were so simple that they could probe the buffer
memory to detect completed requests.  That means they never actually
reclaimed buffers from the used ring.

The qvirtqueue_get_buf() function will allow more sophisticated test
cases to be written without probing bytes in multiple buffers.

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 tests/libqos/virtio.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/libqos/virtio.h |  2 ++
 2 files changed, 81 insertions(+)
diff mbox

Patch

diff --git a/tests/libqos/virtio.c b/tests/libqos/virtio.c
index bc9c24c..948d722 100644
--- a/tests/libqos/virtio.c
+++ b/tests/libqos/virtio.c
@@ -141,6 +141,7 @@  void qvring_init(const QGuestAllocator *alloc, QVirtQueue *vq, uint64_t addr)
         + vq->align - 1) & ~(vq->align - 1));
     vq->free_head = 0;
     vq->num_free = vq->size;
+    vq->last_used_idx = 0;
 
     for (i = 0; i < vq->size - 1; i++) {
         /* vq->desc[i].addr */
@@ -267,6 +268,84 @@  uint32_t qvirtqueue_add_indirect(QVirtQueue *vq, QVRingIndirectDesc *indirect,
     return idx;
 }
 
+static uint16_t get_desc_flags(QVirtQueue *vq, uint16_t idx)
+{
+    return readw(vq->desc + idx * sizeof(struct vring_desc) +
+                 offsetof(struct vring_desc, flags));
+}
+
+static uint16_t get_desc_next(QVirtQueue *vq, uint16_t idx)
+{
+    return readw(vq->desc + idx * sizeof(struct vring_desc) +
+                 offsetof(struct vring_desc, next));
+}
+
+static void set_desc_next(QVirtQueue *vq, uint16_t idx, uint16_t val)
+{
+    writew(vq->desc + idx * sizeof(struct vring_desc) +
+           offsetof(struct vring_desc, next), val);
+}
+
+static void free_descs(QVirtQueue *vq, uint16_t head)
+{
+    uint16_t idx = head;
+
+    for (idx = head;
+         get_desc_flags(vq, idx) & VRING_DESC_F_NEXT;
+         idx = get_desc_next(vq, idx)) {
+        vq->num_free++;
+    }
+    vq->num_free++; /* also count the final descriptor */
+
+    /* Add descriptors to free list */
+    set_desc_next(vq, idx, vq->free_head);
+    vq->free_head = head;
+}
+
+/**
+ * qvirtqueue_get_buf:
+ * @vq: the virtqueue
+ * @len: the number of bytes written by the device
+ *
+ * Get the next used buffer from a virtqueue.
+ *
+ * Returns: the token given to qvirtqueue_add()/qvirtqueue_add_indirect() or
+ * NULL if there are no more buffers.
+ */
+void *qvirtqueue_get_buf(QVirtQueue *vq, unsigned int *len)
+{
+    unsigned int head;
+    void *token;
+    /* vq->used->idx */
+    uint16_t idx = readw(vq->used + offsetof(struct vring_used, idx));
+
+    if (vq->last_used_idx == idx) {
+        return NULL;
+    }
+
+    idx = vq->last_used_idx % vq->size;
+
+    /* vq->used->ring[idx].id */
+    head = readl(vq->used + offsetof(struct vring_used, ring) +
+                 sizeof(struct vring_used_elem) * idx);
+    g_assert_cmpint(head, <, vq->size);
+
+    g_assert(vq->tokens[head]);
+    token = vq->tokens[head];
+    vq->tokens[head] = NULL;
+
+    free_descs(vq, head);
+
+    /* vq->used->ring[idx].len */
+    *len = readl(vq->used + offsetof(struct vring_used, ring) +
+                 sizeof(struct vring_used_elem) * idx +
+                 offsetof(struct vring_used_elem, len));
+
+    vq->last_used_idx++;
+
+    return token;
+}
+
 void qvirtqueue_kick(const QVirtioBus *bus, QVirtioDevice *d, QVirtQueue *vq,
                                                             uint32_t free_head)
 {
diff --git a/tests/libqos/virtio.h b/tests/libqos/virtio.h
index b5154a9..4fea8ef 100644
--- a/tests/libqos/virtio.h
+++ b/tests/libqos/virtio.h
@@ -27,6 +27,7 @@  typedef struct QVirtQueue {
     uint64_t avail; /* This points to a struct vring_avail */
     uint64_t used; /* This points to a struct vring_desc */
     uint16_t index;
+    uint16_t last_used_idx;
     uint32_t size;
     uint32_t free_head;
     uint32_t num_free;
@@ -131,6 +132,7 @@  uint32_t qvirtqueue_add(QVirtQueue *vq, uint64_t data, uint32_t len, bool write,
                         bool next, void *token);
 uint32_t qvirtqueue_add_indirect(QVirtQueue *vq, QVRingIndirectDesc *indirect,
                                  void *token);
+void *qvirtqueue_get_buf(QVirtQueue *vq, unsigned int *len);
 void qvirtqueue_kick(const QVirtioBus *bus, QVirtioDevice *d, QVirtQueue *vq,
                                                             uint32_t free_head);