]> err.no Git - linux-2.6/commitdiff
USB: ehci completes high speed ISO URBs sooner
authorDavid Brownell <david-b@pacbell.net>
Mon, 7 Jan 2008 08:47:42 +0000 (00:47 -0800)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 1 Feb 2008 22:35:02 +0000 (14:35 -0800)
This has some bugfixes for the EHCI driver's ISO transfer scanning
logic.  It was leaving ITDs and SITDs on the schedule too long, for
a few different reasons, which caused trouble.

  (a) Look at all microframes for high speed transfers, not just
the ones we expect to have finished.  This way transfers
ending mid-frame will complete without needing another IRQ.
This also minimizes bogus scheduling underruns (e.g. EL2NSYNC).

  (b) When we encounter an ISO transfer (either speed, but this
hits mostly at full speed) that's not yet been completed,
immediately stop scanning; we've caught up to the hardware,
no matter what other indications might say.

  (c) Always clean up ITDs (for high speed transfers) when the HC
is no longer running.

I'm not sure whether the last one has been observed before, but both
the others have been reported with "real world" audio and video code.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/host/ehci-sched.c

index 001b2c389be2352b52d2de5dd96341f94a2ad539..8a8e08a51ba33cd55b57c127d550d7c54e40a5ca 100644 (file)
@@ -2123,17 +2123,9 @@ scan_periodic (struct ehci_hcd *ehci)
        for (;;) {
                union ehci_shadow       q, *q_p;
                __hc32                  type, *hw_p;
-               unsigned                uframes;
+               unsigned                incomplete = false;
 
-               /* don't scan past the live uframe */
                frame = now_uframe >> 3;
-               if (frame == (clock >> 3))
-                       uframes = now_uframe & 0x07;
-               else {
-                       /* safe to scan the whole frame at once */
-                       now_uframe |= 0x07;
-                       uframes = 8;
-               }
 
 restart:
                /* scan each element in frame's queue for completions */
@@ -2171,12 +2163,15 @@ restart:
                                q = q.fstn->fstn_next;
                                break;
                        case Q_TYPE_ITD:
-                               /* skip itds for later in the frame */
+                               /* If this ITD is still active, leave it for
+                                * later processing ... check the next entry.
+                                */
                                rmb ();
-                               for (uf = live ? uframes : 8; uf < 8; uf++) {
+                               for (uf = 0; uf < 8 && live; uf++) {
                                        if (0 == (q.itd->hw_transaction [uf]
                                                        & ITD_ACTIVE(ehci)))
                                                continue;
+                                       incomplete = true;
                                        q_p = &q.itd->itd_next;
                                        hw_p = &q.itd->hw_next;
                                        type = Q_NEXT_TYPE(ehci,
@@ -2184,10 +2179,12 @@ restart:
                                        q = *q_p;
                                        break;
                                }
-                               if (uf != 8)
+                               if (uf < 8 && live)
                                        break;
 
-                               /* this one's ready ... HC won't cache the
+                               /* Take finished ITDs out of the schedule
+                                * and process them:  recycle, maybe report
+                                * URB completion.  HC won't cache the
                                 * pointer for much longer, if at all.
                                 */
                                *q_p = q.itd->itd_next;
@@ -2198,8 +2195,12 @@ restart:
                                q = *q_p;
                                break;
                        case Q_TYPE_SITD:
+                               /* If this SITD is still active, leave it for
+                                * later processing ... check the next entry.
+                                */
                                if ((q.sitd->hw_results & SITD_ACTIVE(ehci))
                                                && live) {
+                                       incomplete = true;
                                        q_p = &q.sitd->sitd_next;
                                        hw_p = &q.sitd->hw_next;
                                        type = Q_NEXT_TYPE(ehci,
@@ -2207,6 +2208,11 @@ restart:
                                        q = *q_p;
                                        break;
                                }
+
+                               /* Take finished SITDs out of the schedule
+                                * and process them:  recycle, maybe report
+                                * URB completion.
+                                */
                                *q_p = q.sitd->sitd_next;
                                *hw_p = q.sitd->hw_next;
                                type = Q_NEXT_TYPE(ehci, q.sitd->hw_next);
@@ -2232,7 +2238,14 @@ restart:
                        }
                }
 
-               /* stop when we catch up to the HC */
+               /* If we can tell we caught up to the hardware, stop now.
+                * We can't advance our scan without collecting the ISO
+                * transfers that are still pending in this frame.
+                */
+               if (incomplete && HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) {
+                       ehci->next_uframe = now_uframe;
+                       break;
+               }
 
                // FIXME:  this assumes we won't get lapped when
                // latencies climb; that should be rare, but...