From: Ming Lei <tom.leiming@xxxxxxxxx> The current ehci ISO schedule only supports that the max irq delay for ehci is 20ms(see 'if (excess >= mod - 2 * SCHEDULE_SLOP)' in iso_stream_schedule). And if ehci irq is disabled for longer time than 20ms, ehci ISO schedule will behave badly and cause failure of "request %p would overflow". This patch uses kernel time to check if iso schedule was handled late, and fixes the problem. Certainly, the problem can be fixed by increasing the allowable delay, such as: if (excess >= mod - N * SCHEDULE_SLOP) /*iso_stream_schedule*/ and take a larger value of N than 2, but the fix is not feasible enough, also may decrease the allowable ISO schedule window, especially for small periodic size case(such as, 256 and 512). The method in this patch can fix all the above shortcomings, and the only drawback is that it may introduce extra computation on ktime. Signed-off-by: Ming Lei <tom.leiming@xxxxxxxxx> --- drivers/usb/host/ehci-sched.c | 17 +++++++++++++++-- drivers/usb/host/ehci.h | 1 + 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 6c9fbe3..1846cbe 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1432,16 +1432,17 @@ iso_stream_schedule ( else next = now; - /* Fell behind (by up to twice the slop amount)? + /* Fell behind (by up to any time) * We decide based on the time of the last currently-scheduled * slot, not the time of the next available slot. */ excess = (stream->next_uframe - period - next) & (mod - 1); - if (excess >= mod - 2 * SCHEDULE_SLOP) + if (ktime_us_delta(stream->next_ktime, ktime_get()) < 0) start = next + excess - mod + period * DIV_ROUND_UP(mod - excess, period); else start = next + excess + period; + if (start - now >= mod) { ehci_dbg(ehci, "request %p would overflow (%d+%d >= %d)\n", urb, start - now - period, period, @@ -1591,6 +1592,17 @@ itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd) *hw_p = cpu_to_hc32(ehci, itd->itd_dma | Q_TYPE_ITD); } +static void get_next_uframe_ktime(struct ehci_hcd *ehci, + struct ehci_iso_stream *stream) +{ + unsigned mod = ehci->periodic_size << 3; + u32 now = + ehci_readl(ehci, &ehci->regs->frame_index) & (mod - 1); + + stream->next_ktime = ktime_add_us(ktime_get(), + ((stream->next_uframe - now) & (mod - 1)) * 125); +} + /* fit urb's itds into the selected schedule slot; activate as needed */ static int itd_link_urb ( @@ -1658,6 +1670,7 @@ itd_link_urb ( } } stream->next_uframe = next_uframe; + get_next_uframe_ktime(ehci, stream); /* don't need that schedule data any more */ iso_sched_free (stream, iso_sched); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index bd6ff48..a46054a 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -417,6 +417,7 @@ struct ehci_iso_stream { /* output of (re)scheduling */ int next_uframe; + ktime_t next_ktime; __hc32 splits; /* the rest is derived from the endpoint descriptor, -- 1.7.4.1 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html