aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host
diff options
context:
space:
mode:
authorPavankumar Kondeti <pkondeti@codeaurora.org>2012-09-07 11:23:28 +0530
committerBen Hutchings <ben@decadent.org.uk>2012-09-19 15:05:20 +0100
commit3bef14ff773afb2b26c05b30418a0b891563b0a9 (patch)
tree2927ade3c224dc4f0ec1af89f2ca7fb084df47cf /drivers/usb/host
parent10b4b8b20803ba45a77fec99a311bed774decc71 (diff)
downloadkernel_samsung_smdk4412-3bef14ff773afb2b26c05b30418a0b891563b0a9.zip
kernel_samsung_smdk4412-3bef14ff773afb2b26c05b30418a0b891563b0a9.tar.gz
kernel_samsung_smdk4412-3bef14ff773afb2b26c05b30418a0b891563b0a9.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> Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
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 fef1db3..2023733 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -128,9 +128,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)