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