Allocate the arrays for in_addr/out_addr/in_sg/out_sg outside the VirtQueueElement. For now, virtqueue_pop and vring_pop keep allocating a very large VirtQueueElement.
Signed-off-by: Paolo Bonzini <pbonz...@redhat.com> --- hw/virtio/dataplane/vring.c | 2 +- hw/virtio/virtio.c | 60 +++++++++++++++++++++++++++++++-------------- include/hw/virtio/virtio.h | 9 ++++--- 3 files changed, 47 insertions(+), 24 deletions(-) diff --git a/hw/virtio/dataplane/vring.c b/hw/virtio/dataplane/vring.c index 1b900fc..c950caa 100644 --- a/hw/virtio/dataplane/vring.c +++ b/hw/virtio/dataplane/vring.c @@ -402,7 +402,7 @@ void *vring_pop(VirtIODevice *vdev, Vring *vring, size_t sz) goto out; } - elem = g_malloc(sz); + elem = virtqueue_alloc_element(sz, VIRTQUEUE_MAX_SIZE, VIRTQUEUE_MAX_SIZE); /* Initialize elem so it can be safely unmapped */ elem->in_num = elem->out_num = 0; diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index f5f8108..32c89eb 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -494,11 +494,29 @@ static void virtqueue_map_iovec(struct iovec *sg, hwaddr *addr, void virtqueue_map(VirtQueueElement *elem) { virtqueue_map_iovec(elem->in_sg, elem->in_addr, &elem->in_num, - MIN(ARRAY_SIZE(elem->in_sg), ARRAY_SIZE(elem->in_addr)), - 1); + VIRTQUEUE_MAX_SIZE, 1); virtqueue_map_iovec(elem->out_sg, elem->out_addr, &elem->out_num, - MIN(ARRAY_SIZE(elem->out_sg), ARRAY_SIZE(elem->out_addr)), - 0); + VIRTQUEUE_MAX_SIZE, 0); +} + +void *virtqueue_alloc_element(size_t sz, unsigned out_num, unsigned in_num) +{ + VirtQueueElement *elem; + size_t in_addr_ofs = QEMU_ALIGN_UP(sz, __alignof__(elem->in_addr[0])); + size_t out_addr_ofs = in_addr_ofs + in_num * sizeof (elem->in_addr[0]); + size_t out_addr_end = out_addr_ofs + out_num * sizeof (elem->out_addr[0]); + size_t in_sg_ofs = QEMU_ALIGN_UP(out_addr_end, __alignof__(elem->in_sg[0])); + size_t out_sg_ofs = in_sg_ofs + in_num * sizeof (elem->in_sg[0]); + size_t out_sg_end = out_sg_ofs + out_num * sizeof (elem->out_sg[0]); + + elem = g_malloc(out_sg_end); + elem->out_num = out_num; + elem->in_num = in_num; + elem->in_addr = (void*)elem + in_addr_ofs; + elem->out_addr = (void*)elem + out_addr_ofs; + elem->in_sg = (void*)elem + in_sg_ofs; + elem->out_sg = (void*)elem + out_sg_ofs; + return elem; } void *virtqueue_pop(VirtQueue *vq, size_t sz) @@ -513,7 +531,7 @@ void *virtqueue_pop(VirtQueue *vq, size_t sz) } /* When we start there are none of either input nor output. */ - elem = g_malloc(sz); + elem = virtqueue_alloc_element(sz, VIRTQUEUE_MAX_SIZE, VIRTQUEUE_MAX_SIZE); elem->out_num = elem->in_num = 0; max = vq->vring.num; @@ -540,14 +558,14 @@ void *virtqueue_pop(VirtQueue *vq, size_t sz) struct iovec *sg; if (vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_WRITE) { - if (elem->in_num >= ARRAY_SIZE(elem->in_sg)) { + if (elem->in_num >= VIRTQUEUE_MAX_SIZE) { error_report("Too many write descriptors in indirect table"); exit(1); } elem->in_addr[elem->in_num] = vring_desc_addr(vdev, desc_pa, i); sg = &elem->in_sg[elem->in_num++]; } else { - if (elem->out_num >= ARRAY_SIZE(elem->out_sg)) { + if (elem->out_num >= VIRTQUEUE_MAX_SIZE) { error_report("Too many read descriptors in indirect table"); exit(1); } @@ -577,31 +595,35 @@ void *virtqueue_pop(VirtQueue *vq, size_t sz) void *qemu_get_virtqueue_element(QEMUFile *f, size_t sz) { - VirtQueueElement *elem = g_malloc(sz); + VirtQueueElement *elem; bool swap; hwaddr addr[VIRTQUEUE_MAX_SIZE]; struct iovec iov[VIRTQUEUE_MAX_SIZE]; + uint32_t index, out_num, in_num; uint64_t scratch; int i; - qemu_get_be32s(f, &elem->index); - qemu_get_be32s(f, &elem->out_num); - qemu_get_be32s(f, &elem->in_num); + qemu_get_be32s(f, &index); + qemu_get_be32s(f, &out_num); + qemu_get_be32s(f, &in_num); - swap = (elem->out_num & 0xFFFF0000) || (elem->in_num & 0xFFFF0000); + swap = (out_num & 0xFFFF0000) || (in_num & 0xFFFF0000); if (swap) { - bswap32s(&elem->index); - bswap32s(&elem->out_num); - bswap32s(&elem->in_num); + bswap32s(&index); + bswap32s(&out_num); + bswap32s(&in_num); } + elem = virtqueue_alloc_element(sz, out_num, in_num); + elem->index = index; + for (i = 0; i < elem->in_num; i++) { qemu_get_be64s(f, &elem->in_addr[i]); if (swap) { bswap64s(&elem->in_addr[i]); } } - if (i < ARRAY_SIZE(addr)) { + if (i < VIRTQUEUE_MAX_SIZE) { qemu_get_buffer(f, (uint8_t *)addr, sizeof(addr) - i * sizeof(addr[0])); } @@ -611,7 +633,7 @@ void *qemu_get_virtqueue_element(QEMUFile *f, size_t sz) bswap64s(&elem->out_addr[i]); } } - if (i < ARRAY_SIZE(addr)) { + if (i < VIRTQUEUE_MAX_SIZE) { qemu_get_buffer(f, (uint8_t *)addr, sizeof(addr) - i * sizeof(addr[0])); } @@ -623,7 +645,7 @@ void *qemu_get_virtqueue_element(QEMUFile *f, size_t sz) } elem->in_sg[i].iov_len = scratch; } - if (i < ARRAY_SIZE(iov)) { + if (i < VIRTQUEUE_MAX_SIZE) { qemu_get_buffer(f, (uint8_t *)iov, sizeof(iov) - i * sizeof(iov[0])); } @@ -635,7 +657,7 @@ void *qemu_get_virtqueue_element(QEMUFile *f, size_t sz) } elem->out_sg[i].iov_len = scratch; } - if (i < ARRAY_SIZE(iov)) { + if (i < VIRTQUEUE_MAX_SIZE) { qemu_get_buffer(f, (uint8_t *)iov, sizeof(iov) - i * sizeof(iov[0])); } diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 44da9a8..108cdb0 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -46,10 +46,10 @@ typedef struct VirtQueueElement unsigned int index; unsigned int out_num; unsigned int in_num; - hwaddr in_addr[VIRTQUEUE_MAX_SIZE]; - hwaddr out_addr[VIRTQUEUE_MAX_SIZE]; - struct iovec in_sg[VIRTQUEUE_MAX_SIZE]; - struct iovec out_sg[VIRTQUEUE_MAX_SIZE]; + hwaddr *in_addr; + hwaddr *out_addr; + struct iovec *in_sg; + struct iovec *out_sg; } VirtQueueElement; #define VIRTIO_QUEUE_MAX 1024 @@ -143,6 +143,7 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size, void virtio_del_queue(VirtIODevice *vdev, int n); +void *virtqueue_alloc_element(size_t sz, unsigned out_num, unsigned in_num); void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem, unsigned int len); void virtqueue_flush(VirtQueue *vq, unsigned int count); -- 1.8.3.1