summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2013-04-05 12:05:49 +0200
committerHans de Goede <hdegoede@redhat.com>2013-04-08 14:38:59 +0200
commit2d608503da16560546ed6c0f04cac87601856412 (patch)
tree332b8f36e9b56f196af8d31f87fc7bfd07ec6026
parentf0a3e522543763cc5126283031309fdf6f8787c2 (diff)
ehci_free_packet: Discard finished packets when the queue is haltedqemu-kvm-1.2-f18
With pipelining it is possible to encounter a finished packet when cleaning the queue due to a halt. This happens when a non stall error happens while talking to a real device. In this case the queue on the usb-host side will continue processing packets, and we can have completed packets waiting in the queue after an error condition packet causing a halt. There are 2 reasons to discard the completed packets at this point, rather then writing them back to the guest: 1) The guest expect to be able to cancel and/or change packets after the packet with the error without doing an unlink, so writing them back may confuse the guest. 2) Since the queue does not advance when halted, the writing back of these packets will trigger an assert because p->qtdaddr != q->qtdaddr, causing qemu to abort. Note that discarding these packets means that the guest driver and the device will get out of sync! This is unfortunate, but should not be a problem since with a non stall error (iow an io-error) the 2 are out of sync already anyways. Still this patch adds a warning printf to signal this happening. Note that sofar this has only been seen with a DVB-T receiver, which gives of a MPEG-2 stream, which allows for recovering from lost packets, see: https://bugzilla.redhat.com/show_bug.cgi?id=890320 Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-rw-r--r--hw/usb/hcd-ehci.c18
1 files changed, 11 insertions, 7 deletions
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index 6be11c5d9..6a787aa04 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -752,11 +752,10 @@ static EHCIPacket *ehci_alloc_packet(EHCIQueue *q)
static void ehci_free_packet(EHCIPacket *p)
{
- if (p->async == EHCI_ASYNC_FINISHED) {
+ if (p->async == EHCI_ASYNC_FINISHED &&
+ !(p->queue->qh.token & QTD_TOKEN_HALT)) {
EHCIQueue *q = p->queue;
int state = ehci_get_state(q->ehci, q->async);
- /* This is a normal, but rare condition (cancel racing completion) */
- fprintf(stderr, "EHCI: Warning packet completed but not processed\n");
ehci_state_executing(q);
ehci_state_writeback(q);
if (!(q->qh.token & QTD_TOKEN_HALT)) {
@@ -767,12 +766,17 @@ static void ehci_free_packet(EHCIPacket *p)
return;
}
trace_usb_ehci_packet_action(p->queue, p, "free");
- if (p->async == EHCI_ASYNC_INITIALIZED) {
- usb_packet_unmap(&p->packet, &p->sgl);
- qemu_sglist_destroy(&p->sgl);
- }
if (p->async == EHCI_ASYNC_INFLIGHT) {
usb_cancel_packet(&p->packet);
+ }
+ if (p->async == EHCI_ASYNC_FINISHED) {
+ fprintf(stderr,
+ "EHCI: Dropping completed packet from halted %s ep %02X\n",
+ (p->pid == USB_TOKEN_IN) ? "in" : "out",
+ get_field(p->queue->qh.epchar, QH_EPCHAR_EP));
+
+ }
+ if (p->async != EHCI_ASYNC_NONE) {
usb_packet_unmap(&p->packet, &p->sgl);
qemu_sglist_destroy(&p->sgl);
}