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

[PATCH] ehci, remove potential hangs

These don't affect the hang I'm hunting for, but paranoia
argues the patch is better integrated than not:

- prevent resubmit-from-completion looping in_irq if the
   transfers complete really fast.  (likely never seen, but...)

- grab ehci lock before reading irq status; should be harmless
   except in one host error cleanup-after-death
parent 8e4f2cd3
......@@ -637,9 +637,13 @@ static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs)
static void ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u32 status = readl (&ehci->regs->status);
u32 status;
int bh;
spin_lock (&ehci->lock);
status = readl (&ehci->regs->status);
/* e.g. cardbus physical eject */
if (status == ~(u32) 0) {
ehci_dbg (ehci, "device removed\n");
......@@ -648,9 +652,7 @@ static void ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
status &= INTR_MASK;
if (!status) /* irq sharing? */
return;
spin_lock (&ehci->lock);
goto done;
/* clear (just) interrupts */
writel (status, &ehci->regs->status);
......@@ -693,6 +695,7 @@ static void ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
if (bh)
ehci_work (ehci, regs);
done:
spin_unlock (&ehci->lock);
}
......
......@@ -222,7 +222,7 @@ ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb, struct pt_regs *regs)
static unsigned
qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, struct pt_regs *regs)
{
struct ehci_qtd *last = 0;
struct ehci_qtd *last = 0, *end = qh->dummy;
struct list_head *entry, *tmp;
int stopped = 0;
unsigned count = 0;
......@@ -253,6 +253,10 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, struct pt_regs *regs)
last = 0;
}
/* ignore urbs submitted during completions we reported */
if (qtd == end)
break;
/* hardware copies qtd out of qh overlay */
rmb ();
token = le32_to_cpu (qtd->hw_token);
......@@ -967,25 +971,28 @@ static void
scan_async (struct ehci_hcd *ehci, struct pt_regs *regs)
{
struct ehci_qh *qh;
unsigned count;
if (!++(ehci->stamp))
ehci->stamp++;
rescan:
qh = ehci->async->qh_next.qh;
count = 0;
if (likely (qh != 0)) {
do {
/* clean any finished work for this qh */
if (!list_empty (&qh->qtd_list)) {
if (!list_empty (&qh->qtd_list)
&& qh->stamp != ehci->stamp) {
int temp;
/* unlinks could happen here; completion
* reporting drops the lock.
* reporting drops the lock. rescan using
* the latest schedule, but don't rescan
* qhs we already finished (no looping).
*/
qh = qh_get (qh);
qh->stamp = ehci->stamp;
temp = qh_completions (ehci, qh, regs);
qh_put (ehci, qh);
if (temp != 0) {
count += temp;
goto rescan;
}
}
......
......@@ -81,6 +81,7 @@ struct ehci_hcd { /* one per controller */
struct pci_pool *sitd_pool; /* sitd per split iso urb */
struct timer_list watchdog;
unsigned stamp;
#ifdef EHCI_STATS
struct ehci_stats stats;
......@@ -306,6 +307,7 @@ struct ehci_qh {
struct ehci_qtd *dummy;
atomic_t refcount;
unsigned stamp;
u8 qh_state;
#define QH_STATE_LINKED 1 /* HC sees this */
......
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