Commit f0a71006 authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[PATCH] USB: ehci-hcd, partial VIA workaround

This patch resolves a FIXME, which happens to make many of
the VIA problems act significantly less severe.  The change
defers unlinking any QH that just became idle, since it's not
unlikely it'll be used again before many milliseconds pass.

It reduces the number of unlink interrupts (IAA), and means
fewer re-activation issues.  Like: newly queued TDs being
all or partially processed before the QH gets de-activated.
The VIA hardware seems to have some problems in those cases.
(Which are extremely common on 2.4 kernels, and less so on
2.5 because usb-storage streams data much better now.)

It also starts tracking the "lost IAA" errors that I see on
at least one VT8235 motherboard.  It shows in the "registers"
sysfs file.  It'd be good to know if it's just my hardware
that has this problem, or if other folk also see it.
parent 328a5259
...@@ -605,8 +605,10 @@ show_registers (struct device *dev, char *buf) ...@@ -605,8 +605,10 @@ show_registers (struct device *dev, char *buf)
} }
#ifdef EHCI_STATS #ifdef EHCI_STATS
temp = snprintf (next, size, "irq normal %ld err %ld reclaim %ld\n", temp = snprintf (next, size,
ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim); "irq normal %ld err %ld reclaim %ld (lost %ld)\n",
ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim,
ehci->stats.lost_iaa);
size -= temp; size -= temp;
next += temp; next += temp;
......
...@@ -260,6 +260,7 @@ static void ehci_watchdog (unsigned long param) ...@@ -260,6 +260,7 @@ static void ehci_watchdog (unsigned long param)
if (status & STS_IAA) { if (status & STS_IAA) {
ehci_vdbg (ehci, "lost IAA\n"); ehci_vdbg (ehci, "lost IAA\n");
COUNT (ehci->stats.lost_iaa);
writel (STS_IAA, &ehci->regs->status); writel (STS_IAA, &ehci->regs->status);
ehci->reclaim_ready = 1; ehci->reclaim_ready = 1;
} }
...@@ -547,8 +548,9 @@ static void ehci_stop (struct usb_hcd *hcd) ...@@ -547,8 +548,9 @@ static void ehci_stop (struct usb_hcd *hcd)
ehci_mem_cleanup (ehci); ehci_mem_cleanup (ehci);
#ifdef EHCI_STATS #ifdef EHCI_STATS
ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld\n", ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld (lost %ld)\n",
ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim); ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim,
ehci->stats.lost_iaa);
ehci_dbg (ehci, "complete %ld unlink %ld\n", ehci_dbg (ehci, "complete %ld unlink %ld\n",
ehci->stats.complete, ehci->stats.unlink); ehci->stats.complete, ehci->stats.unlink);
#endif #endif
......
...@@ -800,6 +800,7 @@ static struct ehci_qh *qh_append_tds ( ...@@ -800,6 +800,7 @@ static struct ehci_qh *qh_append_tds (
&& !usb_pipecontrol (urb->pipe)) { && !usb_pipecontrol (urb->pipe)) {
/* "never happens": drivers do stall cleanup right */ /* "never happens": drivers do stall cleanup right */
if (qh->qh_state != QH_STATE_IDLE if (qh->qh_state != QH_STATE_IDLE
&& !list_empty (&qh->qtd_list)
&& qh->qh_state != QH_STATE_COMPLETING) && qh->qh_state != QH_STATE_COMPLETING)
ehci_warn (ehci, "clear toggle dev%d " ehci_warn (ehci, "clear toggle dev%d "
"ep%d%s: not idle\n", "ep%d%s: not idle\n",
...@@ -1014,6 +1015,7 @@ static void ...@@ -1014,6 +1015,7 @@ static void
scan_async (struct ehci_hcd *ehci, struct pt_regs *regs) scan_async (struct ehci_hcd *ehci, struct pt_regs *regs)
{ {
struct ehci_qh *qh; struct ehci_qh *qh;
int unlink_delay = 0;
if (!++(ehci->stamp)) if (!++(ehci->stamp))
ehci->stamp++; ehci->stamp++;
...@@ -1040,17 +1042,25 @@ scan_async (struct ehci_hcd *ehci, struct pt_regs *regs) ...@@ -1040,17 +1042,25 @@ scan_async (struct ehci_hcd *ehci, struct pt_regs *regs)
} }
} }
/* unlink idle entries, reducing HC PCI usage as /* unlink idle entries, reducing HC PCI usage as well
* well as HCD schedule-scanning costs. * as HCD schedule-scanning costs. delay for any qh
* * we just scanned, there's a not-unusual case that it
* FIXME don't unlink idle entries so quickly; it * doesn't stay idle for long.
* can penalize (common) half duplex protocols. * (plus, avoids some kind of re-activation race.)
*/ */
if (list_empty (&qh->qtd_list) && !ehci->reclaim) { if (list_empty (&qh->qtd_list)) {
if (qh->stamp == ehci->stamp)
unlink_delay = 1;
else if (!ehci->reclaim) {
start_unlink_async (ehci, qh); start_unlink_async (ehci, qh);
unlink_delay = 0;
}
} }
qh = qh->qh_next.qh; qh = qh->qh_next.qh;
} while (qh); } while (qh);
} }
if (unlink_delay && !timer_pending (&ehci->watchdog))
mod_timer (&ehci->watchdog, jiffies + EHCI_WATCHDOG_JIFFIES/2);
} }
...@@ -27,6 +27,7 @@ struct ehci_stats { ...@@ -27,6 +27,7 @@ struct ehci_stats {
unsigned long normal; unsigned long normal;
unsigned long error; unsigned long error;
unsigned long reclaim; unsigned long reclaim;
unsigned long lost_iaa;
/* termination of urbs from core */ /* termination of urbs from core */
unsigned long complete; unsigned long complete;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment