forked from luck/tmp_suning_uos_patched
USB: EHCI: fix bug in Iso scheduling
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>
This commit is contained in:
parent
d1f114d12b
commit
b40e43fcc5
@ -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?
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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
|
||||
@ -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;
|
||||
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user