uhci: Always mark a queue valid when we encounter it

Before this patch we would not mark a queue valid when its head was a
non-active td. This causes us to misbehave in the following scenario:

1) queue with multiple input transfers queued
2) We hit some latency issue, causing qemu to get behind processing frames
3) When qemu gets to run again, it notices the first transfer ends short,
   marking the head td non-active
4) It now processes 32+ frames in a row without giving the guest a chance
   to run since it is behind
5) valid is decreased to 0, causing the queue to get cancelled also cancelling
   already queued up further input transfers
6) guest gets to run, notices the inactive td, cleanups up further tds
   from the short transfer, and lets the queue continue at the first td of
   the next input transfer
7) we re-start the queue, issuing the second input transfer for the *second*
   time, and any data read by the first time we issued it has been lost

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:31:18 +02:00 committed by Gerd Hoffmann
parent 420ca987d5
commit 3905097ea8

View File

@ -183,6 +183,9 @@ static UHCIQueue *uhci_queue_new(UHCIState *s, uint32_t qh_addr, UHCI_TD *td,
queue->ep = ep; queue->ep = ep;
QTAILQ_INIT(&queue->asyncs); QTAILQ_INIT(&queue->asyncs);
QTAILQ_INSERT_HEAD(&s->queues, queue, next); QTAILQ_INSERT_HEAD(&s->queues, queue, next);
/* valid needs to be large enough to handle 10 frame delay
* for initial isochronous requests */
queue->valid = 32;
trace_usb_uhci_queue_add(queue->token); trace_usb_uhci_queue_add(queue->token);
return queue; return queue;
} }
@ -819,6 +822,10 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
} }
} }
if (q) {
q->valid = 32;
}
/* Is active ? */ /* Is active ? */
if (!(td->ctrl & TD_CTRL_ACTIVE)) { if (!(td->ctrl & TD_CTRL_ACTIVE)) {
if (async) { if (async) {
@ -836,9 +843,6 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
} }
if (async) { if (async) {
/* Already submitted */
async->queue->valid = 32;
if (!async->done) if (!async->done)
return TD_RESULT_ASYNC_CONT; return TD_RESULT_ASYNC_CONT;
if (queuing) { if (queuing) {
@ -860,11 +864,6 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
} }
async = uhci_async_alloc(q, td_addr); async = uhci_async_alloc(q, td_addr);
/* valid needs to be large enough to handle 10 frame delay
* for initial isochronous requests
*/
async->queue->valid = 32;
max_len = ((td->token >> 21) + 1) & 0x7ff; max_len = ((td->token >> 21) + 1) & 0x7ff;
spd = (pid == USB_TOKEN_IN && (td->ctrl & TD_CTRL_SPD) != 0); spd = (pid == USB_TOKEN_IN && (td->ctrl & TD_CTRL_SPD) != 0);
usb_packet_setup(&async->packet, pid, q->ep, td_addr, spd, usb_packet_setup(&async->packet, pid, q->ep, td_addr, spd,