On Thu, 24 Apr 2014, Dennis New wrote: > So, it crashed again after almost a week. But ohci-pci seemed to have > gotten in an infinite loop, constantly repeating about 30 times per > second: > > [256283.572813] ohci-pci 0000:00:13.1: OHCI SF watchdog triggered > [256283.572819] ohci-pci 0000:00:13.1: Frame counter has stopped at 6263 > [256283.596153] ohci-pci 0000:00:13.1: OHCI SF watchdog triggered > [256283.596163] ohci-pci 0000:00:13.1: Frame counter has stopped at 6263 Guess I should have put a limit on the number of repetitions. > The frame counter did indeed stop there. Here's a few of the logs I > captured: > > http://dennisn.linuxd.org/guest/pubstuff/debug-usbaudio/crash6.log I see. I can't tell whether those strange resets and "new devices" have any connection to this, so for the time being I'll ignore them. All right, here's a slightly different version of the patch. This one should shut down the controller entirely when the problem occurs and the frame pointer stops. After another week or so, I'll expect to see your next report... Alan Stern Index: usb-3.14/drivers/usb/host/ohci.h =================================================================== --- usb-3.14.orig/drivers/usb/host/ohci.h +++ usb-3.14/drivers/usb/host/ohci.h @@ -408,6 +408,8 @@ struct ohci_hcd { // there are also chip quirks/bugs in init logic struct work_struct nec_work; /* Worker for NEC quirk */ + struct timer_list sf_watchdog; + unsigned sf_tick; /* Needed for ZF Micro quirk */ struct timer_list unlink_watchdog; Index: usb-3.14/drivers/usb/host/ohci-hcd.c =================================================================== --- usb-3.14.orig/drivers/usb/host/ohci-hcd.c +++ usb-3.14/drivers/usb/host/ohci-hcd.c @@ -76,6 +76,7 @@ static const char hcd_name [] = "ohci_hc #include "ohci.h" #include "pci-quirks.h" +static void enable_sf_interrupt(struct ohci_hcd *ohci); static void ohci_dump (struct ohci_hcd *ohci, int verbose); static void ohci_stop (struct usb_hcd *hcd); @@ -416,6 +417,57 @@ static int check_ed(struct ohci_hcd *ohc && !list_empty(&ed->td_list); } +/* + * Sometimes OHCI controllers fail to issue Start-of-Frame interrupts. + * There are two main reasons for this to happen: the controller crashes + * without a UE interrupt, or the controller turns off its frame counter + * (some versions do this when no ports are connected). + * + * Without SF interrupts, the ed_rm_list will never be emptied, which means + * unlinked URBs will never complete. Hence the need for this watchdog + * routine. + */ +static void sf_watchdog_func(unsigned long _ohci) +{ + unsigned long flags; + struct ohci_hcd *ohci = (struct ohci_hcd *) _ohci; + + ohci_err(ohci, "OHCI SF watchdog triggered\n"); + spin_lock_irqsave(&ohci->lock, flags); + if (ohci->sf_tick == ohci_frame_no(ohci)) { + ohci_err(ohci, "Frame counter has stopped at %u\n", + ohci->sf_tick); + ohci_err(ohci, "Disabling OHCI controller\n"); + ohci->rh_state = OHCI_RH_HALTED; + usb_hc_died(ohci_to_hcd(ohci)); + ohci_dump(ohci, 1); + ohci_usb_reset(ohci); + goto done; + } + finish_unlinks(ohci, ohci->sf_tick + 20); + + if ((ohci->ed_rm_list || ohci->ed_to_check) && + ohci->rh_state == OHCI_RH_RUNNING) + enable_sf_interrupt(ohci); + else + ohci_writel(ohci, OHCI_INTR_SF, &ohci->regs->intrdisable); + done: + spin_unlock_irqrestore(&ohci->lock, flags); +} + +static void enable_sf_interrupt(struct ohci_hcd *ohci) +{ + + ohci_writel(ohci, OHCI_INTR_SF, &ohci->regs->intrstatus); + ohci_writel(ohci, OHCI_INTR_SF, &ohci->regs->intrenable); + + /* flush those writes */ + (void) ohci_readl(ohci, &ohci->regs->control); + + ohci->sf_tick = ohci_frame_no(ohci); + mod_timer(&ohci->sf_watchdog, jiffies + 1 + msecs_to_jiffies(20)); +} + /* ZF Micro watchdog timer callback. The ZF Micro chipset sometimes completes * an interrupt TD but neglects to add it to the donelist. On systems with * this chipset, we need to periodically check the state of the queues to look @@ -476,14 +528,7 @@ static void unlink_watchdog_func(unsigne * those could defer the IRQ more than one frame, using * DI...) Check again after the next INTR_SF. */ - ohci_writel(ohci, OHCI_INTR_SF, - &ohci->regs->intrstatus); - ohci_writel(ohci, OHCI_INTR_SF, - &ohci->regs->intrenable); - - /* flush those writes */ - (void) ohci_readl(ohci, &ohci->regs->control); - + enable_sf_interrupt(ohci); goto out; } } @@ -506,6 +551,9 @@ static int ohci_init (struct ohci_hcd *o int ret; struct usb_hcd *hcd = ohci_to_hcd(ohci); + setup_timer(&ohci->sf_watchdog, sf_watchdog_func, + (unsigned long) ohci); + if (distrust_firmware) ohci->flags |= OHCI_QUIRK_HUB_POWER; @@ -825,6 +873,7 @@ static irqreturn_t ohci_irq (struct usb_ usb_hc_died(hcd); } + del_timer(&ohci->sf_watchdog); ohci_dump (ohci, 1); ohci_usb_reset (ohci); } @@ -902,11 +951,13 @@ static irqreturn_t ohci_irq (struct usb_ spin_lock (&ohci->lock); if (ohci->ed_rm_list) finish_unlinks (ohci, ohci_frame_no(ohci)); - if ((ints & OHCI_INTR_SF) != 0 - && !ohci->ed_rm_list - && !ohci->ed_to_check - && ohci->rh_state == OHCI_RH_RUNNING) + if ((ohci->ed_rm_list || ohci->ed_to_check) && + ohci->rh_state == OHCI_RH_RUNNING) + enable_sf_interrupt(ohci); + else if ((ints & OHCI_INTR_SF) != 0) { ohci_writel (ohci, OHCI_INTR_SF, ®s->intrdisable); + del_timer(&ohci->sf_watchdog); + } spin_unlock (&ohci->lock); if (ohci->rh_state == OHCI_RH_RUNNING) { @@ -935,6 +986,7 @@ static void ohci_stop (struct usb_hcd *h free_irq(hcd->irq, hcd); hcd->irq = 0; + del_timer_sync(&ohci->sf_watchdog); if (quirk_zfmicro(ohci)) del_timer(&ohci->unlink_watchdog); if (quirk_amdiso(ohci)) Index: usb-3.14/drivers/usb/host/ohci-hub.c =================================================================== --- usb-3.14.orig/drivers/usb/host/ohci-hub.c +++ usb-3.14/drivers/usb/host/ohci-hub.c @@ -87,6 +87,7 @@ __acquires(ohci->lock) msleep (8); spin_lock_irq (&ohci->lock); } + del_timer(&ohci->sf_watchdog); dl_done_list (ohci); finish_unlinks (ohci, ohci_frame_no(ohci)); @@ -221,7 +222,7 @@ skip_resume: /* interrupts might have been disabled */ ohci_writel (ohci, OHCI_INTR_INIT, &ohci->regs->intrenable); if (ohci->ed_rm_list) - ohci_writel (ohci, OHCI_INTR_SF, &ohci->regs->intrenable); + enable_sf_interrupt(ohci); /* Then re-enable operations */ ohci_writel (ohci, OHCI_USB_OPER, &ohci->regs->control); Index: usb-3.14/drivers/usb/host/ohci-q.c =================================================================== --- usb-3.14.orig/drivers/usb/host/ohci-q.c +++ usb-3.14/drivers/usb/host/ohci-q.c @@ -493,11 +493,7 @@ static void start_ed_unlink (struct ohci ed->ed_prev = NULL; ohci->ed_rm_list = ed; - /* enable SOF interrupt */ - ohci_writel (ohci, OHCI_INTR_SF, &ohci->regs->intrstatus); - ohci_writel (ohci, OHCI_INTR_SF, &ohci->regs->intrenable); - // flush those writes, and get latest HCCA contents - (void) ohci_readl (ohci, &ohci->regs->control); + enable_sf_interrupt(ohci); /* SF interrupt might get delayed; record the frame counter value that * indicates when the HC isn't looking at it, so concurrent unlinks @@ -505,7 +501,6 @@ static void start_ed_unlink (struct ohci * SF is triggered. */ ed->tick = ohci_frame_no(ohci) + 1; - } /*-------------------------------------------------------------------------* -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html