提交 94b037f2 编写于 作者: G Gerd Hoffmann

xhci: use linked list for transfers

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: NGerd Hoffmann <kraxel@redhat.com>
Message-id: 1474965172-30321-5-git-send-email-kraxel@redhat.com
上级 7512b13d
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "hw/hw.h" #include "hw/hw.h"
#include "qemu/timer.h" #include "qemu/timer.h"
#include "qemu/queue.h"
#include "hw/usb.h" #include "hw/usb.h"
#include "hw/pci/pci.h" #include "hw/pci/pci.h"
#include "hw/pci/msi.h" #include "hw/pci/msi.h"
...@@ -46,8 +47,6 @@ ...@@ -46,8 +47,6 @@
#define MAXSLOTS 64 #define MAXSLOTS 64
#define MAXINTRS 16 #define MAXINTRS 16
#define TD_QUEUE 24
/* Very pessimistic, let's hope it's enough for all cases */ /* Very pessimistic, let's hope it's enough for all cases */
#define EV_QUEUE (((3 * 24) + 16) * MAXSLOTS) #define EV_QUEUE (((3 * 24) + 16) * MAXSLOTS)
/* Do not deliver ER Full events. NEC's driver does some things not bound /* Do not deliver ER Full events. NEC's driver does some things not bound
...@@ -346,6 +345,7 @@ typedef struct XHCIPort { ...@@ -346,6 +345,7 @@ typedef struct XHCIPort {
typedef struct XHCITransfer { typedef struct XHCITransfer {
XHCIState *xhci; XHCIState *xhci;
XHCIEPContext *epctx;
USBPacket packet; USBPacket packet;
QEMUSGList sgl; QEMUSGList sgl;
bool running_async; bool running_async;
...@@ -361,7 +361,6 @@ typedef struct XHCITransfer { ...@@ -361,7 +361,6 @@ typedef struct XHCITransfer {
bool timed_xfer; bool timed_xfer;
unsigned int trb_count; unsigned int trb_count;
unsigned int trb_alloced;
XHCITRB *trbs; XHCITRB *trbs;
TRBCCode status; TRBCCode status;
...@@ -371,6 +370,8 @@ typedef struct XHCITransfer { ...@@ -371,6 +370,8 @@ typedef struct XHCITransfer {
unsigned int cur_pkt; unsigned int cur_pkt;
uint64_t mfindex_kick; uint64_t mfindex_kick;
QTAILQ_ENTRY(XHCITransfer) next;
} XHCITransfer; } XHCITransfer;
struct XHCIStreamContext { struct XHCIStreamContext {
...@@ -385,8 +386,8 @@ struct XHCIEPContext { ...@@ -385,8 +386,8 @@ struct XHCIEPContext {
unsigned int epid; unsigned int epid;
XHCIRing ring; XHCIRing ring;
unsigned int next_xfer; uint32_t xfer_count;
XHCITransfer transfers[TD_QUEUE]; QTAILQ_HEAD(, XHCITransfer) transfers;
XHCITransfer *retry; XHCITransfer *retry;
EPType type; EPType type;
dma_addr_t pctx; dma_addr_t pctx;
...@@ -1370,19 +1371,13 @@ static XHCIEPContext *xhci_alloc_epctx(XHCIState *xhci, ...@@ -1370,19 +1371,13 @@ static XHCIEPContext *xhci_alloc_epctx(XHCIState *xhci,
unsigned int epid) unsigned int epid)
{ {
XHCIEPContext *epctx; XHCIEPContext *epctx;
int i;
epctx = g_new0(XHCIEPContext, 1); epctx = g_new0(XHCIEPContext, 1);
epctx->xhci = xhci; epctx->xhci = xhci;
epctx->slotid = slotid; epctx->slotid = slotid;
epctx->epid = epid; epctx->epid = epid;
for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) { QTAILQ_INIT(&epctx->transfers);
epctx->transfers[i].xhci = xhci;
epctx->transfers[i].slotid = slotid;
epctx->transfers[i].epid = epid;
usb_packet_init(&epctx->transfers[i].packet);
}
epctx->kick_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, xhci_ep_kick_timer, epctx); epctx->kick_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, xhci_ep_kick_timer, epctx);
return epctx; return epctx;
...@@ -1443,6 +1438,41 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, ...@@ -1443,6 +1438,41 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
return CC_SUCCESS; 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) static int xhci_ep_nuke_one_xfer(XHCITransfer *t, TRBCCode report)
{ {
int killed = 0; int killed = 0;
...@@ -1469,7 +1499,7 @@ static int xhci_ep_nuke_one_xfer(XHCITransfer *t, TRBCCode report) ...@@ -1469,7 +1499,7 @@ static int xhci_ep_nuke_one_xfer(XHCITransfer *t, TRBCCode report)
g_free(t->trbs); g_free(t->trbs);
t->trbs = NULL; t->trbs = NULL;
t->trb_count = t->trb_alloced = 0; t->trb_count = 0;
return killed; return killed;
} }
...@@ -1479,7 +1509,8 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, ...@@ -1479,7 +1509,8 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid,
{ {
XHCISlot *slot; XHCISlot *slot;
XHCIEPContext *epctx; XHCIEPContext *epctx;
int i, xferi, killed = 0; XHCITransfer *xfer;
int killed = 0;
USBEndpoint *ep = NULL; USBEndpoint *ep = NULL;
assert(slotid >= 1 && slotid <= xhci->numslots); assert(slotid >= 1 && slotid <= xhci->numslots);
assert(epid >= 1 && epid <= 31); assert(epid >= 1 && epid <= 31);
...@@ -1494,14 +1525,16 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, ...@@ -1494,14 +1525,16 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid,
epctx = slot->eps[epid-1]; epctx = slot->eps[epid-1];
xferi = epctx->next_xfer; for (;;) {
for (i = 0; i < TD_QUEUE; i++) { xfer = QTAILQ_FIRST(&epctx->transfers);
killed += xhci_ep_nuke_one_xfer(&epctx->transfers[xferi], report); if (xfer == NULL) {
break;
}
killed += xhci_ep_nuke_one_xfer(xfer, report);
if (killed) { if (killed) {
report = 0; /* Only report once */ report = 0; /* Only report once */
} }
epctx->transfers[xferi].packet.ep = NULL; xhci_ep_free_xfer(xfer);
xferi = (xferi + 1) % TD_QUEUE;
} }
ep = xhci_epid_to_usbep(xhci, slotid, epid); ep = xhci_epid_to_usbep(xhci, slotid, epid);
...@@ -1516,7 +1549,6 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, ...@@ -1516,7 +1549,6 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
{ {
XHCISlot *slot; XHCISlot *slot;
XHCIEPContext *epctx; XHCIEPContext *epctx;
int i;
trace_usb_xhci_ep_disable(slotid, epid); trace_usb_xhci_ep_disable(slotid, epid);
assert(slotid >= 1 && slotid <= xhci->numslots); assert(slotid >= 1 && slotid <= xhci->numslots);
...@@ -1537,10 +1569,6 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, ...@@ -1537,10 +1569,6 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
xhci_free_streams(epctx); 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 */ /* only touch guest RAM if we're not resetting the HC */
if (xhci->dcbaap_low || xhci->dcbaap_high) { if (xhci->dcbaap_low || xhci->dcbaap_high) {
xhci_set_ep_state(xhci, epctx, NULL, EP_DISABLED); xhci_set_ep_state(xhci, epctx, NULL, EP_DISABLED);
...@@ -2104,6 +2132,7 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, ...@@ -2104,6 +2132,7 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
{ {
XHCIStreamContext *stctx; XHCIStreamContext *stctx;
XHCIEPContext *epctx; XHCIEPContext *epctx;
XHCITransfer *xfer;
XHCIRing *ring; XHCIRing *ring;
USBEndpoint *ep = NULL; USBEndpoint *ep = NULL;
uint64_t mfindex; uint64_t mfindex;
...@@ -2168,6 +2197,7 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, ...@@ -2168,6 +2197,7 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
xhci_complete_packet(xfer); xhci_complete_packet(xfer);
} }
assert(!xfer->running_retry); assert(!xfer->running_retry);
xhci_ep_free_xfer(epctx->retry);
epctx->retry = NULL; epctx->retry = NULL;
} }
...@@ -2193,27 +2223,14 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, ...@@ -2193,27 +2223,14 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
assert(ring->dequeue != 0); assert(ring->dequeue != 0);
while (1) { while (1) {
XHCITransfer *xfer = &epctx->transfers[epctx->next_xfer];
if (xfer->running_async || xfer->running_retry) {
break;
}
length = xhci_ring_chain_length(xhci, ring); length = xhci_ring_chain_length(xhci, ring);
if (length < 0) { if (length <= 0) {
break; 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 = xhci_ep_alloc_xfer(epctx, length);
xfer->trbs = g_new(XHCITRB, length); if (xfer == NULL) {
xfer->trb_alloced = length; break;
} }
xfer->trb_count = length;
for (i = 0; i < length; i++) { for (i = 0; i < length; i++) {
TRBType type; TRBType type;
...@@ -2223,25 +2240,19 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, ...@@ -2223,25 +2240,19 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
xfer->streamid = streamid; xfer->streamid = streamid;
if (epid == 1) { if (epid == 1) {
if (xhci_fire_ctl_transfer(xhci, xfer) >= 0) { xhci_fire_ctl_transfer(xhci, xfer);
epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE;
} else {
DPRINTF("xhci: error firing CTL transfer\n");
}
} else { } else {
if (xhci_fire_transfer(xhci, xfer, epctx) >= 0) { xhci_fire_transfer(xhci, xfer, epctx);
epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE; }
} else { if (xfer->complete) {
if (!xfer->timed_xfer) { xhci_ep_free_xfer(xfer);
DPRINTF("xhci: error firing data transfer\n"); xfer = NULL;
}
}
} }
if (epctx->state == EP_HALTED) { if (epctx->state == EP_HALTED) {
break; break;
} }
if (xfer->running_retry) { if (xfer != NULL && xfer->running_retry) {
DPRINTF("xhci: xfer nacked, stopping schedule\n"); DPRINTF("xhci: xfer nacked, stopping schedule\n");
epctx->retry = xfer; epctx->retry = xfer;
break; break;
...@@ -3480,6 +3491,9 @@ static void xhci_complete(USBPort *port, USBPacket *packet) ...@@ -3480,6 +3491,9 @@ static void xhci_complete(USBPort *port, USBPacket *packet)
} }
xhci_complete_packet(xfer); xhci_complete_packet(xfer);
xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid, xfer->streamid); 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) static void xhci_child_detach(USBPort *uport, USBDevice *child)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册