aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host
diff options
context:
space:
mode:
authorPavankumar Kondeti <pkondeti@codeaurora.org>2012-09-07 11:23:28 +0530
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-10-02 09:47:38 -0700
commitebf16a5b74e2f91cbfce4179064c35529ba9afad (patch)
tree867ae4f7fd51542b5faf0cfb20a5514185f417e4 /drivers/usb/host
parent863f36bf5ad7edde2ec1196b098fec951fca16d3 (diff)
downloadkernel_samsung_smdk4412-ebf16a5b74e2f91cbfce4179064c35529ba9afad.zip
kernel_samsung_smdk4412-ebf16a5b74e2f91cbfce4179064c35529ba9afad.tar.gz
kernel_samsung_smdk4412-ebf16a5b74e2f91cbfce4179064c35529ba9afad.tar.bz2
EHCI: Update qTD next pointer in QH overlay region during unlink
commit 3d037774b42ed677f699b1dce7d548d55f4e4c2b upstream. 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. Signed-off-by: Pavankumar Kondeti <pkondeti@codeaurora.org> Acked-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/ehci-q.c12
1 files changed, 10 insertions, 2 deletions
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 2499b3b..923153c 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -130,9 +130,17 @@ qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh)
else {
qtd = list_entry (qh->qtd_list.next,
struct ehci_qtd, qtd_list);
- /* first qtd may already be partially processed */
- if (cpu_to_hc32(ehci, qtd->qtd_dma) == qh->hw->hw_current)
+ /*
+ * first qtd may already be partially processed.
+ * If we come here during unlink, the QH overlay region
+ * might have reference to the just unlinked qtd. The
+ * qtd is updated in qh_completions(). Update the QH
+ * overlay here.
+ */
+ if (cpu_to_hc32(ehci, qtd->qtd_dma) == qh->hw->hw_current) {
+ qh->hw->hw_qtd_next = qtd->hw_next;
qtd = NULL;
+ }
}
if (qtd)