Эх сурвалжийг харах

[PATCH] USB: ehci: microframe handling fix

This patch has a one line oops fix, plus related cleanups.

 - The bugfix uses microframe scheduling data given to the hardware to
   test "is this a periodic QH", rather than testing for nonzero period.
   (Prevents an oops by providing the correct answer.)

 - The cleanup going along with the patch should make it clearer what's
   going on whenever those bitfields are accessed.

The bug came about when, around January, two new kinds of EHCI interrupt
scheduling operation were added, involving both the high speed (24 KBytes
per millisec) and low/full speed (1-64 bytes per millisec) microframe
scheduling.  A driver for the Edirol UA-1000 Audio Capture Unit ran into
the oops; it used one of the newly supported high speed modes.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
David Brownell 20 жил өмнө
parent
commit
7dedacf427

+ 1 - 1
drivers/usb/host/ehci-dbg.c

@@ -527,7 +527,7 @@ show_periodic (struct class_device *class_dev, char *buf)
 						p.qh->period,
 						p.qh->period,
 						le32_to_cpup (&p.qh->hw_info2)
 						le32_to_cpup (&p.qh->hw_info2)
 							/* uframe masks */
 							/* uframe masks */
-							& 0xffff,
+							& (QH_CMASK | QH_SMASK),
 						p.qh);
 						p.qh);
 				size -= temp;
 				size -= temp;
 				next += temp;
 				next += temp;

+ 3 - 2
drivers/usb/host/ehci-q.c

@@ -222,7 +222,7 @@ __acquires(ehci->lock)
 		struct ehci_qh	*qh = (struct ehci_qh *) urb->hcpriv;
 		struct ehci_qh	*qh = (struct ehci_qh *) urb->hcpriv;
 
 
 		/* S-mask in a QH means it's an interrupt urb */
 		/* S-mask in a QH means it's an interrupt urb */
-		if ((qh->hw_info2 & __constant_cpu_to_le32 (0x00ff)) != 0) {
+		if ((qh->hw_info2 & __constant_cpu_to_le32 (QH_SMASK)) != 0) {
 
 
 			/* ... update hc-wide periodic stats (for usbfs) */
 			/* ... update hc-wide periodic stats (for usbfs) */
 			ehci_to_hcd(ehci)->self.bandwidth_int_reqs--;
 			ehci_to_hcd(ehci)->self.bandwidth_int_reqs--;
@@ -428,7 +428,8 @@ halt:
 			/* should be rare for periodic transfers,
 			/* should be rare for periodic transfers,
 			 * except maybe high bandwidth ...
 			 * except maybe high bandwidth ...
 			 */
 			 */
-			if (qh->period) {
+			if ((__constant_cpu_to_le32 (QH_SMASK)
+					& qh->hw_info2) != 0) {
 				intr_deschedule (ehci, qh);
 				intr_deschedule (ehci, qh);
 				(void) qh_schedule (ehci, qh);
 				(void) qh_schedule (ehci, qh);
 			} else
 			} else

+ 7 - 6
drivers/usb/host/ehci-sched.c

@@ -301,7 +301,7 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
 
 
 	dev_dbg (&qh->dev->dev,
 	dev_dbg (&qh->dev->dev,
 		"link qh%d-%04x/%p start %d [%d/%d us]\n",
 		"link qh%d-%04x/%p start %d [%d/%d us]\n",
-		period, le32_to_cpup (&qh->hw_info2) & 0xffff,
+		period, le32_to_cpup (&qh->hw_info2) & (QH_CMASK | QH_SMASK),
 		qh, qh->start, qh->usecs, qh->c_usecs);
 		qh, qh->start, qh->usecs, qh->c_usecs);
 
 
 	/* high bandwidth, or otherwise every microframe */
 	/* high bandwidth, or otherwise every microframe */
@@ -385,7 +385,8 @@ static void qh_unlink_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
 
 
 	dev_dbg (&qh->dev->dev,
 	dev_dbg (&qh->dev->dev,
 		"unlink qh%d-%04x/%p start %d [%d/%d us]\n",
 		"unlink qh%d-%04x/%p start %d [%d/%d us]\n",
-		qh->period, le32_to_cpup (&qh->hw_info2) & 0xffff,
+		qh->period,
+		le32_to_cpup (&qh->hw_info2) & (QH_CMASK | QH_SMASK),
 		qh, qh->start, qh->usecs, qh->c_usecs);
 		qh, qh->start, qh->usecs, qh->c_usecs);
 
 
 	/* qh->qh_next still "live" to HC */
 	/* qh->qh_next still "live" to HC */
@@ -411,7 +412,7 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
 	 * active high speed queues may need bigger delays...
 	 * active high speed queues may need bigger delays...
 	 */
 	 */
 	if (list_empty (&qh->qtd_list)
 	if (list_empty (&qh->qtd_list)
-			|| (__constant_cpu_to_le32 (0x0ff << 8)
+			|| (__constant_cpu_to_le32 (QH_CMASK)
 					& qh->hw_info2) != 0)
 					& qh->hw_info2) != 0)
 		wait = 2;
 		wait = 2;
 	else
 	else
@@ -533,7 +534,7 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
 
 
 	/* reuse the previous schedule slots, if we can */
 	/* reuse the previous schedule slots, if we can */
 	if (frame < qh->period) {
 	if (frame < qh->period) {
-		uframe = ffs (le32_to_cpup (&qh->hw_info2) & 0x00ff);
+		uframe = ffs (le32_to_cpup (&qh->hw_info2) & QH_SMASK);
 		status = check_intr_schedule (ehci, frame, --uframe,
 		status = check_intr_schedule (ehci, frame, --uframe,
 				qh, &c_mask);
 				qh, &c_mask);
 	} else {
 	} else {
@@ -569,10 +570,10 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
 		qh->start = frame;
 		qh->start = frame;
 
 
 		/* reset S-frame and (maybe) C-frame masks */
 		/* reset S-frame and (maybe) C-frame masks */
-		qh->hw_info2 &= __constant_cpu_to_le32 (~0xffff);
+		qh->hw_info2 &= __constant_cpu_to_le32(~(QH_CMASK | QH_SMASK));
 		qh->hw_info2 |= qh->period
 		qh->hw_info2 |= qh->period
 			? cpu_to_le32 (1 << uframe)
 			? cpu_to_le32 (1 << uframe)
-			: __constant_cpu_to_le32 (0xff);
+			: __constant_cpu_to_le32 (QH_SMASK);
 		qh->hw_info2 |= c_mask;
 		qh->hw_info2 |= c_mask;
 	} else
 	} else
 		ehci_dbg (ehci, "reused qh %p schedule\n", qh);
 		ehci_dbg (ehci, "reused qh %p schedule\n", qh);

+ 5 - 0
drivers/usb/host/ehci.h

@@ -385,6 +385,11 @@ struct ehci_qh {
 	__le32			hw_info1;        /* see EHCI 3.6.2 */
 	__le32			hw_info1;        /* see EHCI 3.6.2 */
 #define	QH_HEAD		0x00008000
 #define	QH_HEAD		0x00008000
 	__le32			hw_info2;        /* see EHCI 3.6.2 */
 	__le32			hw_info2;        /* see EHCI 3.6.2 */
+#define	QH_SMASK	0x000000ff
+#define	QH_CMASK	0x0000ff00
+#define	QH_HUBADDR	0x007f0000
+#define	QH_HUBPORT	0x3f800000
+#define	QH_MULT		0xc0000000
 	__le32			hw_current;	 /* qtd list - see EHCI 3.6.4 */
 	__le32			hw_current;	 /* qtd list - see EHCI 3.6.4 */
 	
 	
 	/* qtd overlay (hardware parts of a struct ehci_qtd) */
 	/* qtd overlay (hardware parts of a struct ehci_qtd) */