Re: usb audio breaks ohci-pci

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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, &regs->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




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux