Re: [PATCH] EHCI: Update qTD next pointer in QH overlay region during unlink

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

 



On Fri, 7 Sep 2012, Pavankumar Kondeti wrote:

> There is a possibility of QH overlay region having reference to a stale
> qTD pointer during unlink.
> 
> Consider an endpoint having two pending qTD before unlink process begins.
> The endpoint's QH queue looks like this.
> 
> qTD1 --> qTD2 --> Dummy
> 
> To unlink qTD2, QH is removed from asynchronous list and Asynchronous
> Advance Doorbell is programmed.  The qTD1's next qTD pointer is set to
> qTD2'2 next qTD pointer and qTD2 is retired upon controller's doorbell
> interrupt.  If QH's current qTD pointer points to qTD1, transfer overlay
> region still have reference to qTD2. But qtD2 is just unlinked and freed.
> This may cause EHCI system error.  Fix this by updating qTD next pointer
> in QH overlay region with the qTD next pointer of the current qTD.

Pavan:

Your patch got me thinking.  The test we use is this:

	if (cpu_to_hc32(ehci, qtd->qtd_dma) == qh->hw->hw_current) {

But it's possible (although highly unlikely) that this test could
succeed when it shouldn't.  That is, it might happen purely by
coincidence that the qTD at the start of the list had been reused, so
it's really a new TD at the same old address, and the old address is
still in the overlay region.

(To make this happen, you'd have to unlink an URB while it is running,
and the completion handler would have to submit a new URB and
immediately unlink it.  Then the next URB submitted might reuse the qTD
that was running originally.)

It would be better to keep track of whether or not the contents of the
overlay area in an idle QH are active and valid, since that's what we
really want to test here.  Can you read through this patch and verify
that it will do what it's supposed to do?

Thanks,

Alan Stern



Index: usb-3.6/drivers/usb/host/ehci.h
===================================================================
--- usb-3.6.orig/drivers/usb/host/ehci.h
+++ usb-3.6/drivers/usb/host/ehci.h
@@ -401,6 +401,7 @@ struct ehci_qh {
 	struct usb_device	*dev;		/* access to TT */
 	unsigned		is_out:1;	/* bulk or intr OUT */
 	unsigned		clearing_tt:1;	/* Clear-TT-Buf in progress */
+	unsigned		overlay_active:1;	/* no update needed */
 };
 
 /*-------------------------------------------------------------------------*/
Index: usb-3.6/drivers/usb/host/ehci-q.c
===================================================================
--- usb-3.6.orig/drivers/usb/host/ehci-q.c
+++ usb-3.6/drivers/usb/host/ehci-q.c
@@ -135,7 +135,7 @@ qh_refresh (struct ehci_hcd *ehci, struc
 		 * qtd is updated in qh_completions(). Update the QH
 		 * overlay here.
 		 */
-		if (cpu_to_hc32(ehci, qtd->qtd_dma) == qh->hw->hw_current) {
+		if (qh->overlay_active) {
 			qh->hw->hw_qtd_next = qtd->hw_next;
 			qtd = NULL;
 		}
@@ -456,9 +456,7 @@ qh_completions (struct ehci_hcd *ehci, s
 				continue;
 
 			/* qh unlinked; token in overlay may be most current */
-			if (state == QH_STATE_IDLE
-					&& cpu_to_hc32(ehci, qtd->qtd_dma)
-						== hw->hw_current) {
+			if (state == QH_STATE_IDLE && qh->overlay_active) {
 				token = hc32_to_cpu(ehci, hw->hw_token);
 
 				/* An unlink may leave an incomplete
@@ -513,6 +511,15 @@ qh_completions (struct ehci_hcd *ehci, s
 			last->hw_next = qtd->hw_next;
 		}
 
+		/*
+		 * Conversely, if we're removing something at the head
+		 * of the queue then the QH's overlay area can no longer
+		 * be active and valid.
+		 */
+		else {
+			qh->overlay_active = 0;
+		}
+
 		/* remove qtd; it's recycled after possible urb completion */
 		list_del (&qtd->qtd_list);
 		last = qtd;
@@ -1243,6 +1250,8 @@ static void end_unlink_async(struct ehci
 		qh->unlink_next = NULL;
 
 		qh->qh_state = QH_STATE_IDLE;
+		qh->overlay_active = !!(qh->hw->hw_token &
+				cpu_to_hc32(ehci, QTD_STS_ACTIVE));
 		qh->qh_next.qh = NULL;
 
 		qh_completions(ehci, qh);
Index: usb-3.6/drivers/usb/host/ehci-sched.c
===================================================================
--- usb-3.6.orig/drivers/usb/host/ehci-sched.c
+++ usb-3.6/drivers/usb/host/ehci-sched.c
@@ -670,6 +670,8 @@ static void end_unlink_intr(struct ehci_
 	int			rc;
 
 	qh->qh_state = QH_STATE_IDLE;
+	qh->overlay_active = !!(hw->hw_token &
+			cpu_to_hc32(ehci, QTD_STS_ACTIVE));
 	hw->hw_next = EHCI_LIST_END(ehci);
 
 	qh_completions(ehci, qh);


--
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