From patchwork Sun Jan 31 10:29:00 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paolo Bonzini X-Patchwork-Id: 8173241 Return-Path: X-Original-To: patchwork-qemu-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id AA240BEEE5 for ; Sun, 31 Jan 2016 10:29:37 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id BB7B3202FE for ; Sun, 31 Jan 2016 10:29:36 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id C57D9202F0 for ; Sun, 31 Jan 2016 10:29:35 +0000 (UTC) Received: from localhost ([::1]:41099 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aPpGB-0007W6-4G for patchwork-qemu-devel@patchwork.kernel.org; Sun, 31 Jan 2016 05:29:35 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:44994) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aPpFs-0007NT-S6 for qemu-devel@nongnu.org; Sun, 31 Jan 2016 05:29:18 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1aPpFr-0007lq-FI for qemu-devel@nongnu.org; Sun, 31 Jan 2016 05:29:16 -0500 Received: from mail-wm0-x243.google.com ([2a00:1450:400c:c09::243]:35190) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aPpFr-0007ll-4I for qemu-devel@nongnu.org; Sun, 31 Jan 2016 05:29:15 -0500 Received: by mail-wm0-x243.google.com with SMTP id l66so4656293wml.2 for ; Sun, 31 Jan 2016 02:29:15 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references; bh=Ixfc5i1nonZgpMCc18mpeJSyl+8aXZTqdzgtyNKKiR4=; b=S+rI700JkD1rDTKKJmswuiicdGodBBxLCqYaGHt3jg3gjlvZV5fGiYhq+gBpeTretP juUwSLAiNjNbUzVbBTwu38NGxuTekrkFzlbl2sH8W0nDv4k+fyAYOZtqOd1YlUDP8IzH 1N5CJwwePggD9IS4IZGtTZ5f6/Cn2bL6hBlZy8QYe6QcudGk1vH2iWIpYQHyeb4Ywboz kclRUFyGoYeBY4tkYVg8rPyR0ICKpSqEJuR36hEdV6SASMV8hKJ+IzILRVfZleTWGvLR eTMhFcOAyBvVN6rmDuEtTQLxKo8bjBzjEK/kwUQbEzEUsVRhKRKZ+zLzTczmDJZhKPiG F4FQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references; bh=Ixfc5i1nonZgpMCc18mpeJSyl+8aXZTqdzgtyNKKiR4=; b=IvCJ9Q5u9tw2S52qpGiLGID+Y7iisv0hw3Ij/W+z3+VnoGgB+MkOdu7LisJXO9BscY qiwokDuPBkNGPwvSMVay4O+6WRqtQqFzNEO/PqFW8ZDbz4R0uFN36SI8MbeuPJfnqYwv KQjsvsva6gctEz664JONRIpLAwq6B3ax8FCwkeqKNYlnwjo8oseKseRkM5k/I0zfAWEB QPCOhXXQFxB5LD7bCusO5DhafGznXhOn1yVHtmWNshKrSo4eplqrX55iZ2LJxX0d9b2A Fsm2j4RSSlGYvi4sP/9lpxvwFFmXUkf8k4Q5/sG8AXcCd+tEN+TpKdNjD7w0Th2JQJF4 51vQ== X-Gm-Message-State: AG10YOSHtG69nZGGsFUGuYFPAnjn4924G0fABEmSmRm4WhDvtr0yQICRUHFtCAET3B4XJg== X-Received: by 10.194.79.37 with SMTP id g5mr18323581wjx.89.1454236154545; Sun, 31 Jan 2016 02:29:14 -0800 (PST) Received: from donizetti.fosdem.net ([2001:67c:1810:f051:7105:9465:633c:859]) by smtp.gmail.com with ESMTPSA id i79sm2349430wmf.24.2016.01.31.02.29.13 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 31 Jan 2016 02:29:13 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Date: Sun, 31 Jan 2016 11:29:00 +0100 Message-Id: <1454236146-23293-5-git-send-email-pbonzini@redhat.com> X-Mailer: git-send-email 2.5.0 In-Reply-To: <1454236146-23293-1-git-send-email-pbonzini@redhat.com> References: <1454236146-23293-1-git-send-email-pbonzini@redhat.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2a00:1450:400c:c09::243 Cc: cornelia.huck@de.ibm.com, 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+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org X-Spam-Status: No, score=-6.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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. Reviewed-by: Cornelia Huck Signed-off-by: Paolo Bonzini --- v1->v2: add assertions on sz [Conny] hw/virtio/dataplane/vring.c | 3 +- hw/virtio/virtio.c | 110 +++++++++++++++++++++++++++++++++++++++----- include/hw/virtio/virtio.h | 9 ++-- 3 files changed, 105 insertions(+), 17 deletions(-) diff --git a/hw/virtio/dataplane/vring.c b/hw/virtio/dataplane/vring.c index 11e7f9f..c950caa 100644 --- a/hw/virtio/dataplane/vring.c +++ b/hw/virtio/dataplane/vring.c @@ -402,8 +402,7 @@ void *vring_pop(VirtIODevice *vdev, Vring *vring, size_t sz) goto out; } - assert(sz >= sizeof(VirtQueueElement)); - 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 388e91c..f49c5ae 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -494,11 +494,30 @@ 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]); + + assert(sz >= sizeof(VirtQueueElement)); + 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,8 +532,7 @@ void *virtqueue_pop(VirtQueue *vq, size_t sz) } /* When we start there are none of either input nor output. */ - assert(sz >= sizeof(VirtQueueElement)); - 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; @@ -541,14 +559,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); } @@ -576,17 +594,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);