forked from Minki/linux
USB: ehci-hcd unlink speedups
This patch fixes some performance bugs observed with some workloads when unlinking EHCI queue header (QH) descriptors from the async ring (control/bulk schedule). The mechanism intended to defer unlinking an empty QH (so there is no penalty in common cases where it's quickly reused) was not working as intended. Sometimes the unlink was scheduled: - too quickly ... which can be a *strong* negative effect, since that QH becomes unavailable for immediate re-use; - too slowly ... wasting DMA cycles, usually a minor issue except for increased bus contention and power usage; Plus there was an extreme case of "too slowly": a logical error in the IAA watchdog-timer conversion meant that sometimes the unlink never got scheduled. The fix replaces a simple counter with a timestamp derived from the controller's 8 KHz microframe counter, and adjusts the timer usage for some issues associated with HZ being less than 8K. (Based on a patch originally by Alan Stern, and good troubleshooting from Leonid.) Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Cc: Alan Stern <stern@rowland.harvard.edu> Cc: Leonid <leonidv11@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
38f3ad5e74
commit
b963801164
@ -84,7 +84,7 @@ static const char hcd_name [] = "ehci_hcd";
|
||||
#define EHCI_IAA_MSECS 10 /* arbitrary */
|
||||
#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
|
||||
#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */
|
||||
#define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */
|
||||
#define EHCI_SHRINK_FRAMES 5 /* async qh unlink delay */
|
||||
|
||||
/* Initial IRQ latency: faster than hw default */
|
||||
static int log2_irq_thresh = 0; // 0 to 6
|
||||
|
@ -1116,8 +1116,7 @@ static void scan_async (struct ehci_hcd *ehci)
|
||||
struct ehci_qh *qh;
|
||||
enum ehci_timer_action action = TIMER_IO_WATCHDOG;
|
||||
|
||||
if (!++(ehci->stamp))
|
||||
ehci->stamp++;
|
||||
ehci->stamp = ehci_readl(ehci, &ehci->regs->frame_index);
|
||||
timer_action_done (ehci, TIMER_ASYNC_SHRINK);
|
||||
rescan:
|
||||
qh = ehci->async->qh_next.qh;
|
||||
@ -1142,18 +1141,20 @@ rescan:
|
||||
}
|
||||
}
|
||||
|
||||
/* unlink idle entries, reducing HC PCI usage as well
|
||||
/* unlink idle entries, reducing DMA usage as well
|
||||
* as HCD schedule-scanning costs. delay for any qh
|
||||
* we just scanned, there's a not-unusual case that it
|
||||
* doesn't stay idle for long.
|
||||
* (plus, avoids some kind of re-activation race.)
|
||||
*/
|
||||
if (list_empty (&qh->qtd_list)) {
|
||||
if (qh->stamp == ehci->stamp)
|
||||
if (list_empty(&qh->qtd_list)
|
||||
&& qh->qh_state == QH_STATE_LINKED) {
|
||||
if (!ehci->reclaim
|
||||
&& ((ehci->stamp - qh->stamp) & 0x1fff)
|
||||
>= (EHCI_SHRINK_FRAMES * 8))
|
||||
start_unlink_async(ehci, qh);
|
||||
else
|
||||
action = TIMER_ASYNC_SHRINK;
|
||||
else if (!ehci->reclaim
|
||||
&& qh->qh_state == QH_STATE_LINKED)
|
||||
start_unlink_async (ehci, qh);
|
||||
}
|
||||
|
||||
qh = qh->qh_next.qh;
|
||||
|
@ -198,7 +198,10 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
|
||||
break;
|
||||
// case TIMER_ASYNC_SHRINK:
|
||||
default:
|
||||
t = EHCI_SHRINK_JIFFIES;
|
||||
/* add a jiffie since we synch against the
|
||||
* 8 KHz uframe counter.
|
||||
*/
|
||||
t = DIV_ROUND_UP(EHCI_SHRINK_FRAMES * HZ, 1000) + 1;
|
||||
break;
|
||||
}
|
||||
mod_timer(&ehci->watchdog, t + jiffies);
|
||||
|
Loading…
Reference in New Issue
Block a user