ChangeSet 1.879.9.1, 2003/01/06 15:47:26-08:00, david-b@pacbell.net [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 diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c --- a/drivers/usb/host/ehci-hcd.c Wed Jan 8 12:03:21 2003 +++ b/drivers/usb/host/ehci-hcd.c Wed Jan 8 12:03:21 2003 @@ -637,9 +637,13 @@ 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 @@ 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 @@ if (bh) ehci_work (ehci, regs); +done: spin_unlock (&ehci->lock); } diff -Nru a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c --- a/drivers/usb/host/ehci-q.c Wed Jan 8 12:03:21 2003 +++ b/drivers/usb/host/ehci-q.c Wed Jan 8 12:03:21 2003 @@ -222,7 +222,7 @@ 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 @@ 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 @@ 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; } } diff -Nru a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h --- a/drivers/usb/host/ehci.h Wed Jan 8 12:03:21 2003 +++ b/drivers/usb/host/ehci.h Wed Jan 8 12:03:21 2003 @@ -81,6 +81,7 @@ 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_qtd *dummy; atomic_t refcount; + unsigned stamp; u8 qh_state; #define QH_STATE_LINKED 1 /* HC sees this */