From patchwork Fri Jan 15 12:41:52 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 568065 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 55C6C140C03 for ; Fri, 15 Jan 2016 23:44:52 +1100 (AEDT) Received: from localhost ([::1]:46625 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aK3kI-0000Fc-D6 for incoming@patchwork.ozlabs.org; Fri, 15 Jan 2016 07:44:50 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:38067) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aK3hh-0004FA-Fi for qemu-devel@nongnu.org; Fri, 15 Jan 2016 07:42:13 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1aK3hg-0005dY-6X for qemu-devel@nongnu.org; Fri, 15 Jan 2016 07:42:09 -0500 Received: from mx1.redhat.com ([209.132.183.28]:43314) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aK3hf-0005cq-TW for qemu-devel@nongnu.org; Fri, 15 Jan 2016 07:42:08 -0500 Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) by mx1.redhat.com (Postfix) with ESMTPS id 87D178DFFD for ; Fri, 15 Jan 2016 12:42:07 +0000 (UTC) Received: from donizetti.redhat.com (ovpn-112-70.ams2.redhat.com [10.36.112.70]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u0FCfwiU026042; Fri, 15 Jan 2016 07:42:06 -0500 From: Paolo Bonzini To: qemu-devel@nongnu.org Date: Fri, 15 Jan 2016 13:41:52 +0100 Message-Id: <1452861718-25806-5-git-send-email-pbonzini@redhat.com> In-Reply-To: <1452861718-25806-1-git-send-email-pbonzini@redhat.com> References: <1452861718-25806-1-git-send-email-pbonzini@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.24 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 209.132.183.28 Cc: mst@redhat.com Subject: [Qemu-devel] [PATCH 04/10] virtio: introduce virtqueue_alloc_element X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 Reviewed-by: Cornelia Huck --- hw/virtio/dataplane/vring.c | 2 +- hw/virtio/virtio.c | 109 ++++++++++++++++++++++++++++++++++++++++---- include/hw/virtio/virtio.h | 9 ++-- 3 files changed, 105 insertions(+), 15 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 ae3252f..a292ac9 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); } @@ -575,17 +593,87 @@ void *virtqueue_pop(VirtQueue *vq, size_t sz) return elem; } +/* Reading and writing a structure directly to QEMUFile is *awful*, but + * it is what QEMU has always done by mistake. We can change it sooner + * or later by bumping the version number of the affected vm states. + * In the meanwhile, since the in-memory layout of VirtQueueElement + * has changed, we need to marshal to and from the layout that was + * used before the change. + */ +typedef struct VirtQueueElementOld { + 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]; +} VirtQueueElementOld; + void *qemu_get_virtqueue_element(QEMUFile *f, size_t sz) { - VirtQueueElement *elem = g_malloc(sz); - qemu_get_buffer(f, (uint8_t *)elem, sizeof(VirtQueueElement)); + VirtQueueElement *elem; + VirtQueueElementOld data; + int i; + + qemu_get_buffer(f, (uint8_t *)&data, sizeof(VirtQueueElementOld)); + + elem = virtqueue_alloc_element(sz, data.out_num, data.in_num); + elem->index = data.index; + + for (i = 0; i < elem->in_num; i++) { + elem->in_addr[i] = data.in_addr[i]; + } + + for (i = 0; i < elem->out_num; i++) { + elem->out_addr[i] = data.out_addr[i]; + } + + for (i = 0; i < elem->in_num; i++) { + /* Base is overwritten by virtqueue_map. */ + elem->in_sg[i].iov_base = 0; + elem->in_sg[i].iov_len = data.in_sg[i].iov_len; + } + + for (i = 0; i < elem->out_num; i++) { + /* Base is overwritten by virtqueue_map. */ + elem->out_sg[i].iov_base = 0; + elem->out_sg[i].iov_len = data.out_sg[i].iov_len; + } + virtqueue_map(elem); return elem; } void qemu_put_virtqueue_element(QEMUFile *f, VirtQueueElement *elem) { - qemu_put_buffer(f, (uint8_t *)elem, sizeof(VirtQueueElement)); + VirtQueueElementOld data; + int i; + + memset(&data, 0, sizeof(data)); + data.index = elem->index; + data.in_num = elem->in_num; + data.out_num = elem->out_num; + + for (i = 0; i < elem->in_num; i++) { + data.in_addr[i] = elem->in_addr[i]; + } + + for (i = 0; i < elem->out_num; i++) { + data.out_addr[i] = elem->out_addr[i]; + } + + for (i = 0; i < elem->in_num; i++) { + /* Base is overwritten by virtqueue_map when loading. Do not + * save it, as it would leak the QEMU address space layout. */ + data.in_sg[i].iov_len = elem->in_sg[i].iov_len; + } + + for (i = 0; i < elem->out_num; i++) { + /* Do not save iov_base as above. */ + data.out_sg[i].iov_len = elem->out_sg[i].iov_len; + } + qemu_put_buffer(f, (uint8_t *)&data, sizeof(VirtQueueElementOld)); } /* virtio device */ 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);