ehci: Retry to fill the queue while waiting for td completion

If the guest is using multiple transfers to try and keep the usb bus busy /
used at maximum efficiency, currently we would see / do the following:

1) submit transfer 1 to the device
2) submit transfer 2 to the device
3) report transfer 1 completion to guest
4) report transfer 2 completion to guest
5) submit transfer 1 to the device
6) report transfer 1 completion to guest
7) submit transfer 2 to the device
8) report transfer 2 completion to guest
etc.

So after the initial submission we would effectively only have 1 transfer
in flight, rather then 2. This is caused by us not checking the queue for
addition of new transfers by the guest (ie the resubmission of a recently
finished transfer), while waiting for a pending transfer to complete.
This patch does add a check for this, changing the sequence to:

1) submit transfer 1 to the device
2) submit transfer 2 to the device
3) report transfer 1 completion to guest
4) submit transfer 1 to the device
5) report transfer 2 completion to guest
6) submit transfer 2 to the device
etc.

Thus keeping 2 transfers in flight (most of the time, and always 1),
as intended by the guest.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
Hans de Goede 2012-10-24 18:14:04 +02:00 committed by Gerd Hoffmann
parent e3a36bce1d
commit b4ea866499
1 changed files with 6 additions and 5 deletions

View File

@ -381,7 +381,7 @@ struct EHCIQueue {
uint32_t qhaddr; /* address QH read from */ uint32_t qhaddr; /* address QH read from */
uint32_t qtdaddr; /* address QTD read from */ uint32_t qtdaddr; /* address QTD read from */
USBDevice *dev; USBDevice *dev;
QTAILQ_HEAD(, EHCIPacket) packets; QTAILQ_HEAD(pkts_head, EHCIPacket) packets;
}; };
typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead; typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead;
@ -488,6 +488,7 @@ static const char *ehci_mmio_names[] = {
static int ehci_state_executing(EHCIQueue *q); static int ehci_state_executing(EHCIQueue *q);
static int ehci_state_writeback(EHCIQueue *q); static int ehci_state_writeback(EHCIQueue *q);
static int ehci_fill_queue(EHCIPacket *p);
static const char *nr2str(const char **n, size_t len, uint32_t nr) static const char *nr2str(const char **n, size_t len, uint32_t nr)
{ {
@ -1994,7 +1995,7 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
{ {
EHCIqtd qtd; EHCIqtd qtd;
EHCIPacket *p; EHCIPacket *p;
int again = 0; int again = 1;
get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &qtd, get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &qtd,
sizeof(EHCIqtd) >> 2); sizeof(EHCIqtd) >> 2);
@ -2022,7 +2023,6 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
p = NULL; p = NULL;
} }
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
again = 1;
} else if (p != NULL) { } else if (p != NULL) {
switch (p->async) { switch (p->async) {
case EHCI_ASYNC_NONE: case EHCI_ASYNC_NONE:
@ -2031,6 +2031,9 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
ehci_set_state(q->ehci, q->async, EST_EXECUTE); ehci_set_state(q->ehci, q->async, EST_EXECUTE);
break; break;
case EHCI_ASYNC_INFLIGHT: case EHCI_ASYNC_INFLIGHT:
/* Check if the guest has added new tds to the queue */
again = (ehci_fill_queue(QTAILQ_LAST(&q->packets, pkts_head)) ==
USB_RET_PROCERR) ? -1 : 1;
/* Unfinished async handled packet, go horizontal */ /* Unfinished async handled packet, go horizontal */
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
break; break;
@ -2042,13 +2045,11 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
ehci_set_state(q->ehci, q->async, EST_EXECUTING); ehci_set_state(q->ehci, q->async, EST_EXECUTING);
break; break;
} }
again = 1;
} else { } else {
p = ehci_alloc_packet(q); p = ehci_alloc_packet(q);
p->qtdaddr = q->qtdaddr; p->qtdaddr = q->qtdaddr;
p->qtd = qtd; p->qtd = qtd;
ehci_set_state(q->ehci, q->async, EST_EXECUTE); ehci_set_state(q->ehci, q->async, EST_EXECUTE);
again = 1;
} }
return again; return again;