diff options
author | Hans de Goede <hdegoede@redhat.com> | 2012-10-24 18:31:19 +0200 |
---|---|---|
committer | Gerd Hoffmann <kraxel@redhat.com> | 2012-10-25 09:08:12 +0200 |
commit | 8928c9c43df1a0eeda1ae2e9582beb34dce49330 (patch) | |
tree | 8419282d164aa5cd50be54d78a29da553381f862 /hw/usb | |
parent | 3905097ea8ea0e1b3dd1556f3ef74a123fe8622a (diff) |
uhci: 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>
Diffstat (limited to 'hw/usb')
-rw-r--r-- | hw/usb/hcd-uhci.c | 16 |
1 files changed, 13 insertions, 3 deletions
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index beeb3fd7b0..a9e06ef5aa 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -110,7 +110,7 @@ struct UHCIQueue { UHCIState *uhci; USBEndpoint *ep; QTAILQ_ENTRY(UHCIQueue) next; - QTAILQ_HEAD(, UHCIAsync) asyncs; + QTAILQ_HEAD(asyncs_head, UHCIAsync) asyncs; int8_t valid; }; @@ -843,15 +843,25 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, } if (async) { - if (!async->done) - return TD_RESULT_ASYNC_CONT; if (queuing) { /* we are busy filling the queue, we are not prepared to consume completed packages then, just leave them in async state */ return TD_RESULT_ASYNC_CONT; } + if (!async->done) { + UHCI_TD last_td; + UHCIAsync *last = QTAILQ_LAST(&async->queue->asyncs, asyncs_head); + /* + * While we are waiting for the current td to complete, the guest + * may have added more tds to the queue. Note we re-read the td + * rather then caching it, as we want to see guest made changes! + */ + uhci_read_td(s, &last_td, last->td_addr); + uhci_queue_fill(async->queue, &last_td); + return TD_RESULT_ASYNC_CONT; + } uhci_async_unlink(async); goto done; } |