@@ -139,6 +139,7 @@ void qvring_init(const QGuestAllocator *alloc, QVirtQueue *vq, uint64_t addr)
vq->avail = vq->desc + vq->size * sizeof(struct vring_desc);
vq->used = (uint64_t)((vq->avail + sizeof(uint16_t) * (3 + vq->size)
+ vq->align - 1) & ~(vq->align - 1));
+ vq->free_head = 0;
for (i = 0; i < vq->size - 1; i++) {
/* vq->desc[i].addr */
@@ -209,6 +210,8 @@ uint32_t qvirtqueue_add(QVirtQueue *vq, uint64_t data, uint32_t len, bool write,
bool next)
{
uint16_t flags = 0;
+ uint16_t idx = vq->free_head;
+
vq->num_free--;
if (write) {
@@ -220,17 +223,22 @@ uint32_t qvirtqueue_add(QVirtQueue *vq, uint64_t data, uint32_t len, bool write,
}
/* vq->desc[vq->free_head].addr */
- writeq(vq->desc + (16 * vq->free_head), data);
+ writeq(vq->desc + (16 * idx), data);
/* vq->desc[vq->free_head].len */
- writel(vq->desc + (16 * vq->free_head) + 8, len);
+ writel(vq->desc + (16 * idx) + 8, len);
/* vq->desc[vq->free_head].flags */
- writew(vq->desc + (16 * vq->free_head) + 12, flags);
+ writew(vq->desc + (16 * idx) + 12, flags);
- return vq->free_head++; /* Return and increase, in this order */
+ vq->free_head = readw(vq->desc + sizeof(struct vring_desc) * idx +
+ offsetof(struct vring_desc, next));
+
+ return idx;
}
uint32_t qvirtqueue_add_indirect(QVirtQueue *vq, QVRingIndirectDesc *indirect)
{
+ uint16_t idx = vq->free_head;
+
g_assert(vq->indirect);
g_assert_cmpint(vq->size, >=, indirect->elem);
g_assert_cmpint(indirect->index, ==, indirect->elem);
@@ -238,14 +246,17 @@ uint32_t qvirtqueue_add_indirect(QVirtQueue *vq, QVRingIndirectDesc *indirect)
vq->num_free--;
/* vq->desc[vq->free_head].addr */
- writeq(vq->desc + (16 * vq->free_head), indirect->desc);
+ writeq(vq->desc + (16 * idx), indirect->desc);
/* vq->desc[vq->free_head].len */
- writel(vq->desc + (16 * vq->free_head) + 8,
+ writel(vq->desc + (16 * idx) + 8,
sizeof(struct vring_desc) * indirect->elem);
/* vq->desc[vq->free_head].flags */
- writew(vq->desc + (16 * vq->free_head) + 12, VRING_DESC_F_INDIRECT);
+ writew(vq->desc + (16 * idx) + 12, VRING_DESC_F_INDIRECT);
- return vq->free_head++; /* Return and increase, in this order */
+ vq->free_head = readw(vq->desc + sizeof(struct vring_desc) * idx +
+ offsetof(struct vring_desc, next));
+
+ return idx;
}
void qvirtqueue_kick(const QVirtioBus *bus, QVirtioDevice *d, QVirtQueue *vq,
Although qvring_init() chains together all descriptors for the free list, free_head is used as a counter instead of as a free list head. The test cases appear to work because they are so trivial that the free_head counter never exceeds the vring size. If they added more descriptors free_head would cause an out-of-bounds memory access. Change free_head to maintain a descriptor free list. The free list is currently never replenished since there is no function to pop buffers from the used ring. This functionality will be added in a later patch. Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> --- tests/libqos/virtio.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-)