From patchwork Thu Oct 29 09:39:36 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Michael S. Tsirkin" X-Patchwork-Id: 537751 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 759CB140180 for ; Thu, 29 Oct 2015 20:42:31 +1100 (AEDT) Received: from localhost ([::1]:42916 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Zrjj3-0002S3-2O for incoming@patchwork.ozlabs.org; Thu, 29 Oct 2015 05:42:29 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:37789) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZrjgL-0006Sy-Sh for qemu-devel@nongnu.org; Thu, 29 Oct 2015 05:39:43 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZrjgK-0000WW-PN for qemu-devel@nongnu.org; Thu, 29 Oct 2015 05:39:41 -0400 Received: from mx1.redhat.com ([209.132.183.28]:38997) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZrjgK-0000WK-I7 for qemu-devel@nongnu.org; Thu, 29 Oct 2015 05:39:40 -0400 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 63DA5A3275; Thu, 29 Oct 2015 09:39:39 +0000 (UTC) Received: from redhat.com (ovpn-116-32.ams2.redhat.com [10.36.116.32]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with SMTP id t9T9daw4031896; Thu, 29 Oct 2015 05:39:37 -0400 Date: Thu, 29 Oct 2015 11:39:36 +0200 From: "Michael S. Tsirkin" To: qemu-devel@nongnu.org Message-ID: <1446111531-5755-7-git-send-email-mst@redhat.com> References: <1446111531-5755-1-git-send-email-mst@redhat.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <1446111531-5755-1-git-send-email-mst@redhat.com> X-Mutt-Fcc: =sent 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: Peter Maydell , Stefan Hajnoczi , Igor Mammedov Subject: [Qemu-devel] [PULL 06/16] virtio: introduce virtio_map 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 virtio_map_sg currently fails if one of the entries it's mapping is contigious in GPA but not HVA address space. Introduce virtio_map which handles this by splitting sg entries. This new API generally turns out to be a good idea since it's harder to misuse: at least in one case the existing one was used incorrectly. This will still fail if there's no space left in the sg, but luckily max queue size in use is currently 256, while max sg size is 1024, so we should be OK even is all entries happen to cross a single DIMM boundary. Won't work well with very small DIMM sizes, unfortunately: e.g. this will fail with 4K DIMMs where a single request might span a large number of DIMMs. Let's hope these are uncommon - at least we are not breaking things. Note: virtio-scsi calls virtio_map_sg on data loaded from network, and validates input, asserting on failure. Copy the validating code here - it will be dropped from virtio-scsi in a follow-up patch. Reported-by: Igor Mammedov Signed-off-by: Michael S. Tsirkin Reviewed-by: Stefan Hajnoczi Reviewed-by: Igor Mammedov --- include/hw/virtio/virtio.h | 1 + hw/virtio/virtio.c | 56 ++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 9d09115..9d9abb4 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -153,6 +153,7 @@ void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem, void virtqueue_map_sg(struct iovec *sg, hwaddr *addr, size_t num_sg, int is_write); +void virtqueue_map(VirtQueueElement *elem); int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem); int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes, unsigned int out_bytes); diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index d0bc72e..943b990 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -448,28 +448,66 @@ int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes, return in_bytes <= in_total && out_bytes <= out_total; } -void virtqueue_map_sg(struct iovec *sg, hwaddr *addr, - size_t num_sg, int is_write) +static void virtqueue_map_iovec(struct iovec *sg, hwaddr *addr, + unsigned int *num_sg, unsigned int max_size, + int is_write) { unsigned int i; hwaddr len; - if (num_sg > VIRTQUEUE_MAX_SIZE) { - error_report("virtio: map attempt out of bounds: %zd > %d", - num_sg, VIRTQUEUE_MAX_SIZE); - exit(1); - } + /* Note: this function MUST validate input, some callers + * are passing in num_sg values received over the network. + */ + /* TODO: teach all callers that this can fail, and return failure instead + * of asserting here. + * When we do, we might be able to re-enable NDEBUG below. + */ +#ifdef NDEBUG +#error building with NDEBUG is not supported +#endif + assert(*num_sg <= max_size); - for (i = 0; i < num_sg; i++) { + for (i = 0; i < *num_sg; i++) { len = sg[i].iov_len; sg[i].iov_base = cpu_physical_memory_map(addr[i], &len, is_write); - if (sg[i].iov_base == NULL || len != sg[i].iov_len) { + if (!sg[i].iov_base) { error_report("virtio: error trying to map MMIO memory"); exit(1); } + if (len == sg[i].iov_len) { + continue; + } + if (*num_sg >= max_size) { + error_report("virtio: memory split makes iovec too large"); + exit(1); + } + memmove(sg + i + 1, sg + i, sizeof(*sg) * (*num_sg - i)); + memmove(addr + i + 1, addr + i, sizeof(*addr) * (*num_sg - i)); + assert(len < sg[i + 1].iov_len); + sg[i].iov_len = len; + addr[i + 1] += len; + sg[i + 1].iov_len -= len; + ++*num_sg; } } +/* Deprecated: don't use in new code */ +void virtqueue_map_sg(struct iovec *sg, hwaddr *addr, + size_t num_sg, int is_write) +{ + virtqueue_map_iovec(sg, addr, &num_sg, num_sg, is_write); +} + +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_map_iovec(elem->out_sg, elem->out_addr, &elem->out_num, + MIN(ARRAY_SIZE(elem->out_sg), ARRAY_SIZE(elem->out_addr)), + 0); +} + int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem) { unsigned int i, head, max;