Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

EHCI: don't rescan interrupt QHs needlessly

This patch (as1466) speeds up processing of ehci-hcd's periodic list.
The existing code will pointlessly rescan an interrupt endpoint queue
each time it encounters the queue's QH in the periodic list, which can
happen quite a few times if the endpoint's period is low. On some
embedded systems, this useless overhead can waste so much time that
the driver falls hopelessly behind and loses events.

The patch introduces a "periodic_stamp" variable, which gets
incremented each time scan_periodic() runs and each time the scan
advances to a new frame. If the corresponding stamp in an interrupt
QH is equal to the current periodic_stamp, we assume the QH has
already been scanned and skip over it. Otherwise we scan the QH as
usual, and if none of its URBs have completed then we store the
current periodic_stamp in the QH's stamp, preventing it from being
scanned again.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Alan Stern and committed by
Greg Kroah-Hartman
1e12c910 2b7aaf50

+12 -4
+1
drivers/usb/host/ehci-q.c
··· 826 826 is_input, 0, 827 827 hb_mult(maxp) * max_packet(maxp))); 828 828 qh->start = NO_FRAME; 829 + qh->stamp = ehci->periodic_stamp; 829 830 830 831 if (urb->dev->speed == USB_SPEED_HIGH) { 831 832 qh->c_usecs = 0;
+10 -4
drivers/usb/host/ehci-sched.c
··· 2287 2287 } 2288 2288 clock &= mod - 1; 2289 2289 clock_frame = clock >> 3; 2290 + ++ehci->periodic_stamp; 2290 2291 2291 2292 for (;;) { 2292 2293 union ehci_shadow q, *q_p; ··· 2316 2315 temp.qh = qh_get (q.qh); 2317 2316 type = Q_NEXT_TYPE(ehci, q.qh->hw->hw_next); 2318 2317 q = q.qh->qh_next; 2319 - modified = qh_completions (ehci, temp.qh); 2320 - if (unlikely(list_empty(&temp.qh->qtd_list) || 2321 - temp.qh->needs_rescan)) 2322 - intr_deschedule (ehci, temp.qh); 2318 + if (temp.qh->stamp != ehci->periodic_stamp) { 2319 + modified = qh_completions(ehci, temp.qh); 2320 + if (!modified) 2321 + temp.qh->stamp = ehci->periodic_stamp; 2322 + if (unlikely(list_empty(&temp.qh->qtd_list) || 2323 + temp.qh->needs_rescan)) 2324 + intr_deschedule(ehci, temp.qh); 2325 + } 2323 2326 qh_put (temp.qh); 2324 2327 break; 2325 2328 case Q_TYPE_FSTN: ··· 2465 2460 if (ehci->clock_frame != clock_frame) { 2466 2461 free_cached_lists(ehci); 2467 2462 ehci->clock_frame = clock_frame; 2463 + ++ehci->periodic_stamp; 2468 2464 } 2469 2465 } else { 2470 2466 now_uframe++;
+1
drivers/usb/host/ehci.h
··· 118 118 struct timer_list watchdog; 119 119 unsigned long actions; 120 120 unsigned stamp; 121 + unsigned periodic_stamp; 121 122 unsigned random_frame; 122 123 unsigned long next_statechange; 123 124 ktime_t last_periodic_enable;