mirror of https://gitee.com/openkylin/linux.git
EHCI: improved logic for isochronous scheduling
This patch (as1608) reworks the logic used by ehci-hcd for scheduling isochronous transfers. Now the modular calculations are all based on a window that starts at the last frame scanned for isochronous completions. No transfer descriptors for any earlier frames can possibly remain on the schedule, so there can be no confusion from schedule wrap-around. This removes the need for a "slop" region of arbitrary size. There's no need to check for URBs that are longer than the schedule length. With the old code they could throw things off by wrapping around and appearing to end in the near future rather than the distant future. Now such confusion isn't possible, and the existing test for submissions that extend too far into the future will also catch those that exceed the schedule length. (But there still has to be an initial test to handle the case where the schedule already extends as far into the future as possible.) Delays caused by IRQ latency won't confuse the algorithm unless they are ridiculously long (over 250 ms); they will merely reduce how far into the future new transfers can be scheduled. A few people have reported problems caused by delays of 50 ms or so. Now instead of failing completely, isochronous transfers will experience a brief glitch and then continue normally. (Whether this is truly a good thing is debatable. A latency as large as 50 ms generally indicates a bug is present, and complete failure of audio or video transfers draws people's attention pretty vividly. Making the transfers more robust also makes it easier for such bugs to remain undetected.) Finally, ehci->next_frame is renamed to ehci->last_iso_frame, because that better describes what it is: the last frame to have been scanned for isochronous completions. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
6f0c0580b7
commit
c3ee9b76aa
|
@ -1361,7 +1361,7 @@ sitd_slot_ok (
|
||||||
* given EHCI_TUNE_FLS and the slop). Or, write a smarter scheduler!
|
* given EHCI_TUNE_FLS and the slop). Or, write a smarter scheduler!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define SCHEDULE_SLOP 80 /* microframes */
|
#define SCHEDULING_DELAY 40 /* microframes */
|
||||||
|
|
||||||
static int
|
static int
|
||||||
iso_stream_schedule (
|
iso_stream_schedule (
|
||||||
|
@ -1370,7 +1370,7 @@ iso_stream_schedule (
|
||||||
struct ehci_iso_stream *stream
|
struct ehci_iso_stream *stream
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
u32 now, next, start, period, span;
|
u32 now, base, next, start, period, span;
|
||||||
int status;
|
int status;
|
||||||
unsigned mod = ehci->periodic_size << 3;
|
unsigned mod = ehci->periodic_size << 3;
|
||||||
struct ehci_iso_sched *sched = urb->hcpriv;
|
struct ehci_iso_sched *sched = urb->hcpriv;
|
||||||
|
@ -1382,12 +1382,6 @@ iso_stream_schedule (
|
||||||
span <<= 3;
|
span <<= 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (span > mod - SCHEDULE_SLOP) {
|
|
||||||
ehci_dbg (ehci, "iso request %p too long\n", urb);
|
|
||||||
status = -EFBIG;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
now = ehci_read_frame_index(ehci) & (mod - 1);
|
now = ehci_read_frame_index(ehci) & (mod - 1);
|
||||||
|
|
||||||
/* Typical case: reuse current schedule, stream is still active.
|
/* Typical case: reuse current schedule, stream is still active.
|
||||||
|
@ -1396,7 +1390,6 @@ iso_stream_schedule (
|
||||||
* slot in the schedule, implicitly assuming URB_ISO_ASAP.
|
* slot in the schedule, implicitly assuming URB_ISO_ASAP.
|
||||||
*/
|
*/
|
||||||
if (likely (!list_empty (&stream->td_list))) {
|
if (likely (!list_empty (&stream->td_list))) {
|
||||||
u32 excess;
|
|
||||||
|
|
||||||
/* For high speed devices, allow scheduling within the
|
/* For high speed devices, allow scheduling within the
|
||||||
* isochronous scheduling threshold. For full speed devices
|
* isochronous scheduling threshold. For full speed devices
|
||||||
|
@ -1408,36 +1401,41 @@ iso_stream_schedule (
|
||||||
else
|
else
|
||||||
next = now;
|
next = now;
|
||||||
|
|
||||||
/* Fell behind (by up to twice the slop amount)?
|
/*
|
||||||
* We decide based on the time of the last currently-scheduled
|
* Use ehci->last_iso_frame as the base. There can't be any
|
||||||
* slot, not the time of the next available slot.
|
* TDs scheduled for earlier than that.
|
||||||
*/
|
*/
|
||||||
excess = (stream->next_uframe - period - next) & (mod - 1);
|
base = ehci->last_iso_frame << 3;
|
||||||
if (excess >= mod - 2 * SCHEDULE_SLOP)
|
next = (next - base) & (mod - 1);
|
||||||
start = next + excess - mod + period *
|
start = (stream->next_uframe - base) & (mod - 1);
|
||||||
DIV_ROUND_UP(mod - excess, period);
|
|
||||||
else
|
/* Is the schedule already full? */
|
||||||
start = next + excess + period;
|
if (unlikely(start < period)) {
|
||||||
if (start - now >= mod) {
|
ehci_dbg(ehci, "iso sched full %p (%u-%u < %u mod %u)\n",
|
||||||
ehci_dbg(ehci, "request %p would overflow (%d+%d >= %d)\n",
|
urb, stream->next_uframe, base,
|
||||||
urb, start - now - period, period,
|
period, mod);
|
||||||
mod);
|
status = -ENOSPC;
|
||||||
status = -EFBIG;
|
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Behind the scheduling threshold? Assume URB_ISO_ASAP. */
|
||||||
|
if (unlikely(start < next))
|
||||||
|
start += period * DIV_ROUND_UP(next - start, period);
|
||||||
|
|
||||||
|
start += base;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* need to schedule; when's the next (u)frame we could start?
|
/* need to schedule; when's the next (u)frame we could start?
|
||||||
* this is bigger than ehci->i_thresh allows; scheduling itself
|
* this is bigger than ehci->i_thresh allows; scheduling itself
|
||||||
* isn't free, the slop should handle reasonably slow cpus. it
|
* isn't free, the delay should handle reasonably slow cpus. it
|
||||||
* can also help high bandwidth if the dma and irq loads don't
|
* can also help high bandwidth if the dma and irq loads don't
|
||||||
* jump until after the queue is primed.
|
* jump until after the queue is primed.
|
||||||
*/
|
*/
|
||||||
else {
|
else {
|
||||||
int done = 0;
|
int done = 0;
|
||||||
start = SCHEDULE_SLOP + (now & ~0x07);
|
|
||||||
|
|
||||||
/* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */
|
base = now & ~0x07;
|
||||||
|
start = base + SCHEDULING_DELAY;
|
||||||
|
|
||||||
/* find a uframe slot with enough bandwidth.
|
/* find a uframe slot with enough bandwidth.
|
||||||
* Early uframes are more precious because full-speed
|
* Early uframes are more precious because full-speed
|
||||||
|
@ -1464,19 +1462,16 @@ iso_stream_schedule (
|
||||||
|
|
||||||
/* no room in the schedule */
|
/* no room in the schedule */
|
||||||
if (!done) {
|
if (!done) {
|
||||||
ehci_dbg(ehci, "iso resched full %p (now %d max %d)\n",
|
ehci_dbg(ehci, "iso sched full %p", urb);
|
||||||
urb, now, now + mod);
|
|
||||||
status = -ENOSPC;
|
status = -ENOSPC;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tried to schedule too far into the future? */
|
/* Tried to schedule too far into the future? */
|
||||||
if (unlikely(start - now + span - period
|
if (unlikely(start - base + span - period >= mod)) {
|
||||||
>= mod - 2 * SCHEDULE_SLOP)) {
|
ehci_dbg(ehci, "request %p would overflow (%u+%u >= %u)\n",
|
||||||
ehci_dbg(ehci, "request %p would overflow (%d+%d >= %d)\n",
|
urb, start - base, span - period, mod);
|
||||||
urb, start - now, span - period,
|
|
||||||
mod - 2 * SCHEDULE_SLOP);
|
|
||||||
status = -EFBIG;
|
status = -EFBIG;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
@ -1490,7 +1485,7 @@ iso_stream_schedule (
|
||||||
|
|
||||||
/* Make sure scan_isoc() sees these */
|
/* Make sure scan_isoc() sees these */
|
||||||
if (ehci->isoc_count == 0)
|
if (ehci->isoc_count == 0)
|
||||||
ehci->next_frame = now >> 3;
|
ehci->last_iso_frame = now >> 3;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
@ -2220,16 +2215,16 @@ static void scan_isoc(struct ehci_hcd *ehci)
|
||||||
now_frame = (uf >> 3) & fmask;
|
now_frame = (uf >> 3) & fmask;
|
||||||
live = true;
|
live = true;
|
||||||
} else {
|
} else {
|
||||||
now_frame = (ehci->next_frame - 1) & fmask;
|
now_frame = (ehci->last_iso_frame - 1) & fmask;
|
||||||
live = false;
|
live = false;
|
||||||
}
|
}
|
||||||
ehci->now_frame = now_frame;
|
ehci->now_frame = now_frame;
|
||||||
|
|
||||||
frame = ehci->next_frame;
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
union ehci_shadow q, *q_p;
|
union ehci_shadow q, *q_p;
|
||||||
__hc32 type, *hw_p;
|
__hc32 type, *hw_p;
|
||||||
|
|
||||||
|
frame = ehci->last_iso_frame;
|
||||||
restart:
|
restart:
|
||||||
/* scan each element in frame's queue for completions */
|
/* scan each element in frame's queue for completions */
|
||||||
q_p = &ehci->pshadow [frame];
|
q_p = &ehci->pshadow [frame];
|
||||||
|
@ -2334,7 +2329,6 @@ static void scan_isoc(struct ehci_hcd *ehci)
|
||||||
/* Stop when we have reached the current frame */
|
/* Stop when we have reached the current frame */
|
||||||
if (frame == now_frame)
|
if (frame == now_frame)
|
||||||
break;
|
break;
|
||||||
frame = (frame + 1) & fmask;
|
ehci->last_iso_frame = (frame + 1) & fmask;
|
||||||
}
|
}
|
||||||
ehci->next_frame = now_frame;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,7 +143,7 @@ struct ehci_hcd { /* one per controller */
|
||||||
struct ehci_qh *intr_unlink_last;
|
struct ehci_qh *intr_unlink_last;
|
||||||
unsigned intr_unlink_cycle;
|
unsigned intr_unlink_cycle;
|
||||||
unsigned now_frame; /* frame from HC hardware */
|
unsigned now_frame; /* frame from HC hardware */
|
||||||
unsigned next_frame; /* scan periodic, start here */
|
unsigned last_iso_frame; /* last frame scanned for iso */
|
||||||
unsigned intr_count; /* intr activity count */
|
unsigned intr_count; /* intr activity count */
|
||||||
unsigned isoc_count; /* isoc activity count */
|
unsigned isoc_count; /* isoc activity count */
|
||||||
unsigned periodic_count; /* periodic activity count */
|
unsigned periodic_count; /* periodic activity count */
|
||||||
|
|
Loading…
Reference in New Issue