From patchwork Tue Sep 27 08:32:48 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gerd Hoffmann X-Patchwork-Id: 9351605 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 76E63600CB for ; Tue, 27 Sep 2016 08:42:06 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 65F23288DE for ; Tue, 27 Sep 2016 08:42:06 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 5A38629119; Tue, 27 Sep 2016 08:42:06 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 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.wl.linuxfoundation.org (Postfix) with ESMTPS id 93819288DE for ; Tue, 27 Sep 2016 08:42:05 +0000 (UTC) Received: from localhost ([::1]:48787 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bonxk-0007GM-Na for patchwork-qemu-devel@patchwork.kernel.org; Tue, 27 Sep 2016 04:42:04 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:58323) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bonp5-0007Yy-Ho for qemu-devel@nongnu.org; Tue, 27 Sep 2016 04:33:09 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1bonoz-0007zb-Oe for qemu-devel@nongnu.org; Tue, 27 Sep 2016 04:33:06 -0400 Received: from mx1.redhat.com ([209.132.183.28]:34248) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bonoz-0007ye-FP for qemu-devel@nongnu.org; Tue, 27 Sep 2016 04:33:01 -0400 Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 784BB31B313 for ; Tue, 27 Sep 2016 08:33:00 +0000 (UTC) Received: from nilsson.home.kraxel.org (ovpn-116-35.ams2.redhat.com [10.36.116.35]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u8R8Wx5m026156; Tue, 27 Sep 2016 04:32:59 -0400 Received: by nilsson.home.kraxel.org (Postfix, from userid 500) id BC99680BAE; Tue, 27 Sep 2016 10:32:58 +0200 (CEST) From: Gerd Hoffmann To: qemu-devel@nongnu.org Date: Tue, 27 Sep 2016 10:32:48 +0200 Message-Id: <1474965172-30321-5-git-send-email-kraxel@redhat.com> In-Reply-To: <1474965172-30321-1-git-send-email-kraxel@redhat.com> References: <1474965172-30321-1-git-send-email-kraxel@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.24 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.29]); Tue, 27 Sep 2016 08:33:00 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH 4/8] xhci: use linked list for transfers X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Gerd Hoffmann Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP xhci has a fixed number of 24 (TD_QUEUE) XHCITransfer structs per endpoint, which turns out to be a problem for usb3 devices with 32 (or more) bulk streams. xhci re-checks the trb rings on every finished transfer to make sure it'll pick up any pending work. But that scheme breaks in case the first transfer of a ring can't be started because we ran out of XHCITransfer structs already. So remove static XHCITransfer array from XHCIEPContext. Use a linked list instead, and allocate/free XHCITransfer as needed. Add helper functions to allocate & initialize and to cleanup & release XHCITransfer structs. That also simplifies trb management, we never have to realloc XHCITransfer->trbs because we don't reuse XHCITransfer structs any more. New dynamic limit for in-flight xhci transfers per endpoint is number-of-streams + 16. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 122 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 68 insertions(+), 54 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 089dcbf..d8e4074 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "hw/hw.h" #include "qemu/timer.h" +#include "qemu/queue.h" #include "hw/usb.h" #include "hw/pci/pci.h" #include "hw/pci/msi.h" @@ -46,8 +47,6 @@ #define MAXSLOTS 64 #define MAXINTRS 16 -#define TD_QUEUE 24 - /* Very pessimistic, let's hope it's enough for all cases */ #define EV_QUEUE (((3 * 24) + 16) * MAXSLOTS) /* Do not deliver ER Full events. NEC's driver does some things not bound @@ -344,6 +343,7 @@ typedef struct XHCIPort { typedef struct XHCITransfer { XHCIState *xhci; + XHCIEPContext *epctx; USBPacket packet; QEMUSGList sgl; bool running_async; @@ -359,7 +359,6 @@ typedef struct XHCITransfer { bool timed_xfer; unsigned int trb_count; - unsigned int trb_alloced; XHCITRB *trbs; TRBCCode status; @@ -369,6 +368,8 @@ typedef struct XHCITransfer { unsigned int cur_pkt; uint64_t mfindex_kick; + + QTAILQ_ENTRY(XHCITransfer) next; } XHCITransfer; struct XHCIStreamContext { @@ -383,8 +384,8 @@ struct XHCIEPContext { unsigned int epid; XHCIRing ring; - unsigned int next_xfer; - XHCITransfer transfers[TD_QUEUE]; + uint32_t xfer_count; + QTAILQ_HEAD(, XHCITransfer) transfers; XHCITransfer *retry; EPType type; dma_addr_t pctx; @@ -1360,19 +1361,13 @@ static XHCIEPContext *xhci_alloc_epctx(XHCIState *xhci, unsigned int epid) { XHCIEPContext *epctx; - int i; epctx = g_new0(XHCIEPContext, 1); epctx->xhci = xhci; epctx->slotid = slotid; epctx->epid = epid; - for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) { - epctx->transfers[i].xhci = xhci; - epctx->transfers[i].slotid = slotid; - epctx->transfers[i].epid = epid; - usb_packet_init(&epctx->transfers[i].packet); - } + QTAILQ_INIT(&epctx->transfers); epctx->kick_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, xhci_ep_kick_timer, epctx); return epctx; @@ -1433,6 +1428,41 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, return CC_SUCCESS; } +static XHCITransfer *xhci_ep_alloc_xfer(XHCIEPContext *epctx, + uint32_t length) +{ + uint32_t limit = epctx->nr_pstreams + 16; + XHCITransfer *xfer; + + if (epctx->xfer_count >= limit) { + return NULL; + } + + xfer = g_new0(XHCITransfer, 1); + xfer->xhci = epctx->xhci; + xfer->epctx = epctx; + xfer->slotid = epctx->slotid; + xfer->epid = epctx->epid; + xfer->trbs = g_new(XHCITRB, length); + xfer->trb_count = length; + usb_packet_init(&xfer->packet); + + QTAILQ_INSERT_TAIL(&epctx->transfers, xfer, next); + epctx->xfer_count++; + + return xfer; +} + +static void xhci_ep_free_xfer(XHCITransfer *xfer) +{ + QTAILQ_REMOVE(&xfer->epctx->transfers, xfer, next); + xfer->epctx->xfer_count--; + + usb_packet_cleanup(&xfer->packet); + g_free(xfer->trbs); + g_free(xfer); +} + static int xhci_ep_nuke_one_xfer(XHCITransfer *t, TRBCCode report) { int killed = 0; @@ -1459,7 +1489,7 @@ static int xhci_ep_nuke_one_xfer(XHCITransfer *t, TRBCCode report) g_free(t->trbs); t->trbs = NULL; - t->trb_count = t->trb_alloced = 0; + t->trb_count = 0; return killed; } @@ -1469,7 +1499,8 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, { XHCISlot *slot; XHCIEPContext *epctx; - int i, xferi, killed = 0; + XHCITransfer *xfer; + int killed = 0; USBEndpoint *ep = NULL; assert(slotid >= 1 && slotid <= xhci->numslots); assert(epid >= 1 && epid <= 31); @@ -1484,14 +1515,16 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, epctx = slot->eps[epid-1]; - xferi = epctx->next_xfer; - for (i = 0; i < TD_QUEUE; i++) { - killed += xhci_ep_nuke_one_xfer(&epctx->transfers[xferi], report); + for (;;) { + xfer = QTAILQ_FIRST(&epctx->transfers); + if (xfer == NULL) { + break; + } + killed += xhci_ep_nuke_one_xfer(xfer, report); if (killed) { report = 0; /* Only report once */ } - epctx->transfers[xferi].packet.ep = NULL; - xferi = (xferi + 1) % TD_QUEUE; + xhci_ep_free_xfer(xfer); } ep = xhci_epid_to_usbep(xhci, slotid, epid); @@ -1506,7 +1539,6 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, { XHCISlot *slot; XHCIEPContext *epctx; - int i; trace_usb_xhci_ep_disable(slotid, epid); assert(slotid >= 1 && slotid <= xhci->numslots); @@ -1527,10 +1559,6 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, xhci_free_streams(epctx); } - for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) { - usb_packet_cleanup(&epctx->transfers[i].packet); - } - /* only touch guest RAM if we're not resetting the HC */ if (xhci->dcbaap_low || xhci->dcbaap_high) { xhci_set_ep_state(xhci, epctx, NULL, EP_DISABLED); @@ -2094,6 +2122,7 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, { XHCIStreamContext *stctx; XHCIEPContext *epctx; + XHCITransfer *xfer; XHCIRing *ring; USBEndpoint *ep = NULL; uint64_t mfindex; @@ -2158,6 +2187,7 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, xhci_complete_packet(xfer); } assert(!xfer->running_retry); + xhci_ep_free_xfer(epctx->retry); epctx->retry = NULL; } @@ -2183,27 +2213,14 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, assert(ring->dequeue != 0); while (1) { - XHCITransfer *xfer = &epctx->transfers[epctx->next_xfer]; - if (xfer->running_async || xfer->running_retry) { - break; - } length = xhci_ring_chain_length(xhci, ring); - if (length < 0) { + if (length <= 0) { break; - } else if (length == 0) { - break; - } - if (xfer->trbs && xfer->trb_alloced < length) { - xfer->trb_count = 0; - xfer->trb_alloced = 0; - g_free(xfer->trbs); - xfer->trbs = NULL; } - if (!xfer->trbs) { - xfer->trbs = g_new(XHCITRB, length); - xfer->trb_alloced = length; + xfer = xhci_ep_alloc_xfer(epctx, length); + if (xfer == NULL) { + break; } - xfer->trb_count = length; for (i = 0; i < length; i++) { TRBType type; @@ -2213,25 +2230,19 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, xfer->streamid = streamid; if (epid == 1) { - if (xhci_fire_ctl_transfer(xhci, xfer) >= 0) { - epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE; - } else { - DPRINTF("xhci: error firing CTL transfer\n"); - } + xhci_fire_ctl_transfer(xhci, xfer); } else { - if (xhci_fire_transfer(xhci, xfer, epctx) >= 0) { - epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE; - } else { - if (!xfer->timed_xfer) { - DPRINTF("xhci: error firing data transfer\n"); - } - } + xhci_fire_transfer(xhci, xfer, epctx); + } + if (xfer->complete) { + xhci_ep_free_xfer(xfer); + xfer = NULL; } if (epctx->state == EP_HALTED) { break; } - if (xfer->running_retry) { + if (xfer != NULL && xfer->running_retry) { DPRINTF("xhci: xfer nacked, stopping schedule\n"); epctx->retry = xfer; break; @@ -3470,6 +3481,9 @@ static void xhci_complete(USBPort *port, USBPacket *packet) } xhci_complete_packet(xfer); xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid, xfer->streamid); + if (xfer->complete) { + xhci_ep_free_xfer(xfer); + } } static void xhci_child_detach(USBPort *uport, USBDevice *child)