Skip to content

Commit

Permalink
USB: EHCI: fix bug in Iso scheduling
Browse files Browse the repository at this point in the history
This patch (as1098) changes the way ehci-hcd schedules its periodic
Iso transfers.  That the current scheduling code is wrong is clear on
the face of it: Sometimes it returns -EL2NSYNC (meaning that an URB
couldn't be scheduled because it was submitted too late), but it does
this even when the URB_ISO_ASAP flag is set (meaning the URB should be
scheduled as soon as possible).

The new code properly implements as-soon-as-possible scheduling,
assigning the next unexpired slot as the URB's starting point.  It
also is more careful about checking for Iso URB completion: It doesn't
bother to check for activity during frames that are already over,
and it allows for the possibility that some of the URB's packets may
have raced the hardware when they were submitted and so never got used
(the packet status is set to -EXDEV).

This fixes problems several people have experienced with USB video
applications.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Acked-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
  • Loading branch information
Alan Stern authored and Greg Kroah-Hartman committed May 29, 2008
1 parent d1f114d commit b40e43f
Showing 1 changed file with 44 additions and 23 deletions.
67 changes: 44 additions & 23 deletions drivers/usb/host/ehci-sched.c
Original file line number Diff line number Diff line change
Expand Up @@ -1349,18 +1349,27 @@ iso_stream_schedule (
/* when's the last uframe this urb could start? */
max = now + mod;

/* typical case: reuse current schedule. stream is still active,
* and no gaps from host falling behind (irq delays etc)
/* Typical case: reuse current schedule, stream is still active.
* Hopefully there are no gaps from the host falling behind
* (irq delays etc), but if there are we'll take the next
* slot in the schedule, implicitly assuming URB_ISO_ASAP.
*/
if (likely (!list_empty (&stream->td_list))) {
start = stream->next_uframe;
if (start < now)
start += mod;
if (likely ((start + sched->span) < max))
goto ready;
/* else fell behind; someday, try to reschedule */
status = -EL2NSYNC;
goto fail;

/* Fell behind (by up to twice the slop amount)? */
if (start >= max - 2 * 8 * SCHEDULE_SLOP)
start += stream->interval * DIV_ROUND_UP(
max - start, stream->interval) - mod;

/* Tried to schedule too far into the future? */
if (unlikely((start + sched->span) >= max)) {
status = -EFBIG;
goto fail;
}
goto ready;
}

/* need to schedule; when's the next (u)frame we could start?
Expand Down Expand Up @@ -1613,6 +1622,9 @@ itd_complete (
} else if (likely ((t & EHCI_ISOC_ACTIVE) == 0)) {
desc->status = 0;
desc->actual_length = EHCI_ITD_LENGTH (t);
} else {
/* URB was too late */
desc->status = -EXDEV;
}
}

Expand Down Expand Up @@ -2095,7 +2107,7 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb,
static void
scan_periodic (struct ehci_hcd *ehci)
{
unsigned frame, clock, now_uframe, mod;
unsigned now_uframe, frame, clock, clock_frame, mod;
unsigned modified;

mod = ehci->periodic_size << 3;
Expand All @@ -2111,6 +2123,7 @@ scan_periodic (struct ehci_hcd *ehci)
else
clock = now_uframe + mod - 1;
clock %= mod;
clock_frame = clock >> 3;

for (;;) {
union ehci_shadow q, *q_p;
Expand Down Expand Up @@ -2157,22 +2170,26 @@ scan_periodic (struct ehci_hcd *ehci)
case Q_TYPE_ITD:
/* If this ITD is still active, leave it for
* later processing ... check the next entry.
* No need to check for activity unless the
* frame is current.
*/
rmb ();
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,
if (frame == clock_frame && live) {
rmb();
for (uf = 0; uf < 8; uf++) {
if (q.itd->hw_transaction[uf] &
ITD_ACTIVE(ehci))
break;
}
if (uf < 8) {
incomplete = true;
q_p = &q.itd->itd_next;
hw_p = &q.itd->hw_next;
type = Q_NEXT_TYPE(ehci,
q.itd->hw_next);
q = *q_p;
break;
q = *q_p;
break;
}
}
if (uf < 8 && live)
break;

/* Take finished ITDs out of the schedule
* and process them: recycle, maybe report
Expand All @@ -2189,9 +2206,12 @@ scan_periodic (struct ehci_hcd *ehci)
case Q_TYPE_SITD:
/* If this SITD is still active, leave it for
* later processing ... check the next entry.
* No need to check for activity unless the
* frame is current.
*/
if ((q.sitd->hw_results & SITD_ACTIVE(ehci))
&& live) {
if (frame == clock_frame && live &&
(q.sitd->hw_results &
SITD_ACTIVE(ehci))) {
incomplete = true;
q_p = &q.sitd->sitd_next;
hw_p = &q.sitd->hw_next;
Expand Down Expand Up @@ -2260,6 +2280,7 @@ scan_periodic (struct ehci_hcd *ehci)

/* rescan the rest of this frame, then ... */
clock = now;
clock_frame = clock >> 3;
} else {
now_uframe++;
now_uframe %= mod;
Expand Down

0 comments on commit b40e43f

Please sign in to comment.