Re: EHCI reports message "lost IAA" (fwd)

Alan Stern <[email protected]>
Newsgroups gmane.linux.usb.devel,gmane.linux.usb.general
Message-ID <[email protected]>
On Sat, 8 Dec 2007, Dr. Peer Griebel wrote:

> Dear USB developers
> 
> some time ago (3/4 year) I already reported my problem with the EHCI
> driver.  There was no response to it.  I would like to tell you that my
> problem still persists.  I just tried it today using the current Gentoo
> kernel 2.6.23-gentoo-r3.
> 
> There don't seem to be many people with this problem and it is not very
> urgent for me to get it resolved.  But I do think that this problem
> should be resolved.  It is not very easy to find out that there are
> "lost IAA" messages since only the debug version of the driver shows the
> message.  The average user doesn't know about the debug version.  The
> everage user only sees that his web cam does not work.
> 
> I'm a software developer myself.  But I don't know very much about USB.
>  I would be very glad if I can help you in any way to resolve my problem.

On Sat, 8 Dec 2007, Christian Hoffmann wrote:

> Hi,
> I am running into a similar crash with a dvb t receiver in end_async_unlink as
> reported some month ago on this ML.
> 
> PID: 0      TASK: ffffffff804b7870  CPU: 0   COMMAND: "swapper"
>  #0 [ffffffff80624b10] machine_kexec at ffffffff8021c00d
>  #1 [ffffffff80624bc0] crash_kexec at ffffffff80255dbe
>  #2 [ffffffff80624c80] do_page_fault at ffffffff803fde21
>  #3 [ffffffff80624d90] error_exit at ffffffff803fc0fd
>     [exception RIP: end_unlink_async+34]
>     RIP: ffffffff8811d20a  RSP: ffffffff80624e48  RFLAGS: 00010202
>     RAX: 0000000000010029  RBX: ffff81007e098178  RCX: 0000000000000000
>     RDX: 0000000000008028  RSI: ffffffff8811fe7e  RDI: 0000000000000080
>     RBP: 0000000000000000   R8: 0000000000000000   R9: ffffffff8047dc64
>     R10: ffff8100022ffc48  R11: 0000000000000246  R12: 0000000000000000
>     R13: ffff81007e098178  R14: ffff81007e098000  R15: 0000000000000000
>     ORIG_RAX: ffffffffffffffff  CS: 0010  SS: 0018
>  #4 [ffffffff80624e60] ehci_work at ffffffff8811d78b
>  #5 [ffffffff80624ed0] ehci_irq at ffffffff88120d0f
>  #6 [ffffffff80624f20] usb_hcd_irq at ffffffff880ded82
>  #7 [ffffffff80624f30] handle_IRQ_event at ffffffff802603e8
>  #8 [ffffffff80624f60] handle_fasteoi_irq at ffffffff802618b6
>  #9 [ffffffff80624f90] do_IRQ at ffffffff8020c136
> --- <IRQ stack> ---
> #10 [ffffffff805c7e88] ret_from_intr at ffffffff8020a141
>     [exception RIP: acpi_safe_halt+40]
>     RIP: ffffffff88003dc6  RSP: ffffffff805c7f30  RFLAGS: 00000246
>     RAX: 0000000000000000  RBX: ffff81007e6760a8  RCX: ffffffff8047dde4
>     RDX: 0000000000000007  RSI: 00000000000001c6  RDI: 0000000004000000
>     RBP: 0000000000000000   R8: 0000000000000002   R9: ffffffff8047dc64
>     R10: ffff8100022ffc48  R11: 0000000000000246  R12: ffffffff805c7f14
>     R13: 0000000000001001  R14: ffffffff80332334  R15: 0000000000000000
>     ORIG_RAX: ffffffffffffff86  CS: 0010  SS: 0018
> #11 [ffffffff805c7f30] acpi_processor_idle at ffffffff88003ff9
> #12 [ffffffff805c7f70] cpu_idle at ffffffff80208daf
> crash>

> I use 2.6.22.13-0.3-default on an Acer Laptop. ATI chipset. Anything
> else I can provide?

For both of you, here is a somewhat experimental patch you can try.  It 
probably won't fix the problem, but it might make the problem easier to 
diagnose.

Along with this patch you should enable CONFIG_USB_DEBUG.  If a problem 
occurs, the contents of the files in /sys/class/usb_host/usb_hostN 
(where N is the bus number of the EHCI controller in question) will be 
of interest, especially the "async" and "registers" files.

This patch is for 2.6.24-rc4, but it ought to apply with little 
difficulty to all recent kernels.

Alan Stern



Index: 2.6.24-rc4/drivers/usb/host/ehci-dbg.c
===================================================================
--- 2.6.24-rc4.orig/drivers/usb/host/ehci-dbg.c
+++ 2.6.24-rc4/drivers/usb/host/ehci-dbg.c
@@ -763,9 +763,7 @@ show_registers (struct class_device *cla
 	}
 
 	if (ehci->reclaim) {
-		temp = scnprintf (next, size, "reclaim qh %p%s\n",
-				ehci->reclaim,
-				ehci->reclaim_ready ? " ready" : "");
+		temp = scnprintf(next, size, "reclaim qh %p\n", ehci->reclaim);
 		size -= temp;
 		next += temp;
 	}
Index: 2.6.24-rc4/drivers/usb/host/ehci-hcd.c
===================================================================
--- 2.6.24-rc4.orig/drivers/usb/host/ehci-hcd.c
+++ 2.6.24-rc4/drivers/usb/host/ehci-hcd.c
@@ -109,7 +109,7 @@ static const char	hcd_name [] = "ehci_hc
 #define	EHCI_TUNE_MULT_TT	1
 #define	EHCI_TUNE_FLS		2	/* (small) 256 frame schedule */
 
-#define EHCI_IAA_JIFFIES	(HZ/100)	/* arbitrary; ~10 msec */
+#define EHCI_IAA_MSECS		10		/* arbitrary */
 #define EHCI_IO_JIFFIES		(HZ/10)		/* io watchdog > irq_thresh */
 #define EHCI_ASYNC_JIFFIES	(HZ/20)		/* async idle timeout */
 #define EHCI_SHRINK_JIFFIES	(HZ/200)	/* async qh unlink delay */
@@ -266,6 +266,7 @@ static void ehci_quiesce (struct ehci_hc
 
 /*-------------------------------------------------------------------------*/
 
+static void end_unlink_async(struct ehci_hcd *ehci);
 static void ehci_work(struct ehci_hcd *ehci);
 
 #include "ehci-hub.c"
@@ -275,25 +276,41 @@ static void ehci_work(struct ehci_hcd *e
 
 /*-------------------------------------------------------------------------*/
 
-static void ehci_watchdog (unsigned long param)
+static void ehci_iaa_watchdog(unsigned long param)
 {
 	struct ehci_hcd		*ehci = (struct ehci_hcd *) param;
 	unsigned long		flags;
+	u32			status, cmd;
 
 	spin_lock_irqsave (&ehci->lock, flags);
+	WARN_ON(!ehci->reclaim);
 
-	/* lost IAA irqs wedge things badly; seen with a vt8235 */
+	status = ehci_readl(ehci, &ehci->regs->status);
+	cmd = ehci_readl(ehci, &ehci->regs->command);
+	ehci_info(ehci, "IAA watchdog: status %x cmd %x\n", status, cmd);
+
+	/* lost IAA irqs wedge things badly; seen first with a vt8235 */
 	if (ehci->reclaim) {
-		u32		status = ehci_readl(ehci, &ehci->regs->status);
 		if (status & STS_IAA) {
 			ehci_vdbg (ehci, "lost IAA\n");
 			COUNT (ehci->stats.lost_iaa);
 			ehci_writel(ehci, STS_IAA, &ehci->regs->status);
-			ehci->reclaim_ready = 1;
 		}
+		ehci_writel(ehci, cmd & ~CMD_IAAD, &ehci->regs->command);
+		end_unlink_async(ehci);
 	}
 
- 	/* stop async processing after it's idled a bit */
+	spin_unlock_irqrestore(&ehci->lock, flags);
+}
+
+static void ehci_watchdog(unsigned long param)
+{
+	struct ehci_hcd		*ehci = (struct ehci_hcd *) param;
+	unsigned long		flags;
+
+	spin_lock_irqsave(&ehci->lock, flags);
+
+	/* stop async processing after it's idled a bit */
 	if (test_bit (TIMER_ASYNC_OFF, &ehci->actions))
 		start_unlink_async (ehci, ehci->async);
 
@@ -363,8 +380,6 @@ static void ehci_port_power (struct ehci
 static void ehci_work (struct ehci_hcd *ehci)
 {
 	timer_action_done (ehci, TIMER_IO_WATCHDOG);
-	if (ehci->reclaim_ready)
-		end_unlink_async (ehci);
 
 	/* another CPU may drop ehci->lock during a schedule scan while
 	 * it reports urb completions.  this flag guards against bogus
@@ -399,6 +414,7 @@ static void ehci_stop (struct usb_hcd *h
 
 	/* no more interrupts ... */
 	del_timer_sync (&ehci->watchdog);
+	del_timer_sync(&ehci->iaa_watchdog);
 
 	spin_lock_irq(&ehci->lock);
 	if (HC_IS_RUNNING (hcd->state))
@@ -447,6 +463,10 @@ static int ehci_init(struct usb_hcd *hcd
 	ehci->watchdog.function = ehci_watchdog;
 	ehci->watchdog.data = (unsigned long) ehci;
 
+	init_timer(&ehci->iaa_watchdog);
+	ehci->iaa_watchdog.function = ehci_iaa_watchdog;
+	ehci->iaa_watchdog.data = (unsigned long) ehci;
+
 	/*
 	 * hw default: 1K periodic list heads, one per frame.
 	 * periodic_size can shrink by USBCMD update if hcc_params allows.
@@ -463,7 +483,6 @@ static int ehci_init(struct usb_hcd *hcd
 		ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
 
 	ehci->reclaim = NULL;
-	ehci->reclaim_ready = 0;
 	ehci->next_uframe = -1;
 
 	/*
@@ -654,8 +673,7 @@ static irqreturn_t ehci_irq (struct usb_
 	/* complete the unlinking of some qh [4.15.2.3] */
 	if (status & STS_IAA) {
 		COUNT (ehci->stats.reclaim);
-		ehci->reclaim_ready = 1;
-		bh = 1;
+		end_unlink_async(ehci);
 	}
 
 	/* remote wakeup [4.3.1] */
@@ -761,10 +779,16 @@ static int ehci_urb_enqueue (
 
 static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
-	/* if we need to use IAA and it's busy, defer */
-	if (qh->qh_state == QH_STATE_LINKED
-			&& ehci->reclaim
-			&& HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) {
+	/* failfast */
+	if (!HC_IS_RUNNING(ehci_to_hcd(ehci)->state))
+		end_unlink_async(ehci);
+
+	/* if it's not linked then there's nothing to do */
+	if (qh->qh_state != QH_STATE_LINKED)
+		;
+
+	/* defer till later if busy */
+	else if (ehci->reclaim) {
 		struct ehci_qh		*last;
 
 		for (last = ehci->reclaim;
@@ -774,12 +798,8 @@ static void unlink_async (struct ehci_hc
 		qh->qh_state = QH_STATE_UNLINK_WAIT;
 		last->reclaim = qh;
 
-	/* bypass IAA if the hc can't care */
-	} else if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state) && ehci->reclaim)
-		end_unlink_async (ehci);
-
-	/* something else might have unlinked the qh by now */
-	if (qh->qh_state == QH_STATE_LINKED)
+	/* start IAA cycle */
+	} else
 		start_unlink_async (ehci, qh);
 }
 
@@ -806,7 +826,19 @@ static int ehci_urb_dequeue(struct usb_h
 		qh = (struct ehci_qh *) urb->hcpriv;
 		if (!qh)
 			break;
-		unlink_async (ehci, qh);
+		switch (qh->qh_state) {
+		case QH_STATE_LINKED:
+		case QH_STATE_COMPLETING:
+			unlink_async(ehci, qh);
+			break;
+		case QH_STATE_UNLINK:
+		case QH_STATE_UNLINK_WAIT:
+			/* already started */
+			break;
+		case QH_STATE_IDLE:
+			WARN_ON(1);
+			break;
+		}
 		break;
 
 	case PIPE_INTERRUPT:
@@ -898,6 +930,7 @@ rescan:
 		unlink_async (ehci, qh);
 		/* FALL THROUGH */
 	case QH_STATE_UNLINK:		/* wait for hw to finish? */
+	case QH_STATE_UNLINK_WAIT:
 idle_timeout:
 		spin_unlock_irqrestore (&ehci->lock, flags);
 		schedule_timeout_uninterruptible(1);
Index: 2.6.24-rc4/drivers/usb/host/ehci-hub.c
===================================================================
--- 2.6.24-rc4.orig/drivers/usb/host/ehci-hub.c
+++ 2.6.24-rc4/drivers/usb/host/ehci-hub.c
@@ -134,7 +134,7 @@ static int ehci_bus_suspend (struct usb_
 	}
 	ehci->command = ehci_readl(ehci, &ehci->regs->command);
 	if (ehci->reclaim)
-		ehci->reclaim_ready = 1;
+		end_unlink_async(ehci);
 	ehci_work(ehci);
 
 	/* Unlike other USB host controller types, EHCI doesn't have
Index: 2.6.24-rc4/drivers/usb/host/ehci-pci.c
===================================================================
--- 2.6.24-rc4.orig/drivers/usb/host/ehci-pci.c
+++ 2.6.24-rc4/drivers/usb/host/ehci-pci.c
@@ -305,7 +305,7 @@ static int ehci_pci_resume(struct usb_hc
 	/* emptying the schedule aborts any urbs */
 	spin_lock_irq(&ehci->lock);
 	if (ehci->reclaim)
-		ehci->reclaim_ready = 1;
+		end_unlink_async(ehci);
 	ehci_work(ehci);
 	spin_unlock_irq(&ehci->lock);
 
Index: 2.6.24-rc4/drivers/usb/host/ehci-q.c
===================================================================
--- 2.6.24-rc4.orig/drivers/usb/host/ehci-q.c
+++ 2.6.24-rc4/drivers/usb/host/ehci-q.c
@@ -973,7 +973,7 @@ static void end_unlink_async (struct ehc
 	struct ehci_qh		*qh = ehci->reclaim;
 	struct ehci_qh		*next;
 
-	timer_action_done (ehci, TIMER_IAA_WATCHDOG);
+	iaa_watchdog_done(ehci);
 
 	// qh->hw_next = cpu_to_hc32(qh->qh_dma);
 	qh->qh_state = QH_STATE_IDLE;
@@ -983,7 +983,6 @@ static void end_unlink_async (struct ehc
 	/* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
 	next = qh->reclaim;
 	ehci->reclaim = next;
-	ehci->reclaim_ready = 0;
 	qh->reclaim = NULL;
 
 	qh_completions (ehci, qh);
@@ -1059,11 +1058,10 @@ static void start_unlink_async (struct e
 		return;
 	}
 
-	ehci->reclaim_ready = 0;
 	cmd |= CMD_IAAD;
 	ehci_writel(ehci, cmd, &ehci->regs->command);
 	(void)ehci_readl(ehci, &ehci->regs->command);
-	timer_action (ehci, TIMER_IAA_WATCHDOG);
+	iaa_watchdog_start(ehci);
 }
 
 /*-------------------------------------------------------------------------*/
Index: 2.6.24-rc4/drivers/usb/host/ehci.h
===================================================================
--- 2.6.24-rc4.orig/drivers/usb/host/ehci.h
+++ 2.6.24-rc4/drivers/usb/host/ehci.h
@@ -74,7 +74,6 @@ struct ehci_hcd {			/* one per controlle
 	/* async schedule support */
 	struct ehci_qh		*async;
 	struct ehci_qh		*reclaim;
-	unsigned		reclaim_ready : 1;
 	unsigned		scanning : 1;
 
 	/* periodic schedule support */
@@ -105,6 +104,7 @@ struct ehci_hcd {			/* one per controlle
 	struct dma_pool		*itd_pool;	/* itd per iso urb */
 	struct dma_pool		*sitd_pool;	/* sitd per split iso urb */
 
+	struct timer_list	iaa_watchdog;
 	struct timer_list	watchdog;
 	unsigned long		actions;
 	unsigned		stamp;
@@ -140,9 +140,21 @@ static inline struct usb_hcd *ehci_to_hc
 }
 
 
+static inline void
+iaa_watchdog_start(struct ehci_hcd *ehci)
+{
+	WARN_ON(timer_pending(&ehci->iaa_watchdog));
+	mod_timer(&ehci->iaa_watchdog,
+			jiffies + msecs_to_jiffies(EHCI_IAA_MSECS));
+}
+
+static inline void iaa_watchdog_done(struct ehci_hcd *ehci)
+{
+	del_timer(&ehci->iaa_watchdog);
+}
+
 enum ehci_timer_action {
 	TIMER_IO_WATCHDOG,
-	TIMER_IAA_WATCHDOG,
 	TIMER_ASYNC_SHRINK,
 	TIMER_ASYNC_OFF,
 };
@@ -160,9 +172,6 @@ timer_action (struct ehci_hcd *ehci, enu
 		unsigned long t;
 
 		switch (action) {
-		case TIMER_IAA_WATCHDOG:
-			t = EHCI_IAA_JIFFIES;
-			break;
 		case TIMER_IO_WATCHDOG:
 			t = EHCI_IO_JIFFIES;
 			break;
@@ -179,8 +188,7 @@ timer_action (struct ehci_hcd *ehci, enu
 		// async queue SHRINK often precedes IAA.  while it's ready
 		// to go OFF neither can matter, and afterwards the IO
 		// watchdog stops unless there's still periodic traffic.
-		if (action != TIMER_IAA_WATCHDOG
-				&& t > ehci->watchdog.expires
+		if (time_before_eq(t, ehci->watchdog.expires)
 				&& timer_pending (&ehci->watchdog))
 			return;
 		mod_timer (&ehci->watchdog, t);


-------------------------------------------------------------------------
SF.Net email is sponsored by: 
Check out the new SourceForge.net Marketplace.
It's the best place to buy or sell services for
just about anything Open Source.
http://sourceforge.net/services/buy/index.php
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.