brcm2708: update against latest rpi-3.10.y branch
[openwrt/svn-archive/archive.git] / target / linux / brcm2708 / patches-3.10 / 0082-dwc_otg-prevent-OOPSes-during-device-disconnects.patch
1 From dc570a70493daf0ec548ff57f1a1a9fb31caccb7 Mon Sep 17 00:00:00 2001
2 From: P33M <P33M@github.com>
3 Date: Thu, 18 Jul 2013 17:07:26 +0100
4 Subject: [PATCH 082/174] dwc_otg: prevent OOPSes during device disconnects
5
6 The dwc_otg_urb_enqueue function is thread-unsafe. In particular the
7 access of urb->hcpriv, usb_hcd_link_urb_to_ep, dwc_otg_urb->qtd and
8 friends does not occur within a critical section and so if a device
9 was unplugged during activity there was a high chance that the
10 usbcore hub_thread would try to disable the endpoint with partially-
11 formed entries in the URB queue. This would result in BUG() or null
12 pointer dereferences.
13
14 Fix so that access of urb->hcpriv, enqueuing to the hardware and
15 adding to usbcore endpoint URB lists is contained within a single
16 critical section.
17 ---
18 drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 3 ---
19 drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c | 14 +++++---------
20 drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c | 6 +-----
21 3 files changed, 6 insertions(+), 17 deletions(-)
22
23 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
24 +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
25 @@ -464,7 +464,6 @@ int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_
26 dwc_otg_hcd_urb_t * dwc_otg_urb, void **ep_handle,
27 int atomic_alloc)
28 {
29 - dwc_irqflags_t flags;
30 int retval = 0;
31 uint8_t needs_scheduling = 0;
32 dwc_otg_transaction_type_e tr_type;
33 @@ -515,12 +514,10 @@ int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_
34 }
35
36 if(needs_scheduling) {
37 - DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
38 tr_type = dwc_otg_hcd_select_transactions(hcd);
39 if (tr_type != DWC_OTG_TRANSACTION_NONE) {
40 dwc_otg_hcd_queue_transactions(hcd, tr_type);
41 }
42 - DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
43 }
44 return retval;
45 }
46 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c
47 +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c
48 @@ -679,9 +679,7 @@ static int dwc_otg_urb_enqueue(struct us
49 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
50 struct usb_host_endpoint *ep = urb->ep;
51 #endif
52 -#if USB_URB_EP_LINKING
53 dwc_irqflags_t irqflags;
54 -#endif
55 void **ref_ep_hcpriv = &ep->hcpriv;
56 dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
57 dwc_otg_hcd_urb_t *dwc_otg_urb;
58 @@ -733,7 +731,6 @@ static int dwc_otg_urb_enqueue(struct us
59 if(dwc_otg_urb == NULL)
60 return -ENOMEM;
61
62 - urb->hcpriv = dwc_otg_urb;
63 if (!dwc_otg_urb && urb->number_of_packets)
64 return -ENOMEM;
65
66 @@ -775,10 +772,10 @@ static int dwc_otg_urb_enqueue(struct us
67 iso_frame_desc[i].length);
68 }
69
70 -#if USB_URB_EP_LINKING
71 DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &irqflags);
72 + urb->hcpriv = dwc_otg_urb;
73 +#if USB_URB_EP_LINKING
74 retval = usb_hcd_link_urb_to_ep(hcd, urb);
75 - DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, irqflags);
76 if (0 == retval)
77 #endif
78 {
79 @@ -794,17 +791,16 @@ static int dwc_otg_urb_enqueue(struct us
80 urb);
81 }
82 } else {
83 -#if USB_URB_EP_LINKING
84 - dwc_irqflags_t irqflags;
85 DWC_DEBUGPL(DBG_HCD, "DWC OTG dwc_otg_hcd_urb_enqueue failed rc %d\n", retval);
86 - DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &irqflags);
87 +#if USB_URB_EP_LINKING
88 usb_hcd_unlink_urb_from_ep(hcd, urb);
89 - DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, irqflags);
90 #endif
91 + urb->hcpriv = NULL;
92 if (retval == -DWC_E_NO_DEVICE)
93 retval = -ENODEV;
94 }
95 }
96 + DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, irqflags);
97 return retval;
98 }
99
100 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c
101 +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c
102 @@ -919,6 +919,7 @@ void dwc_otg_hcd_qtd_init(dwc_otg_qtd_t
103 * QH to place the QTD into. If it does not find a QH, then it will create a
104 * new QH. If the QH to which the QTD is added is not currently scheduled, it
105 * is placed into the proper schedule based on its EP type.
106 + * HCD lock must be held and interrupts must be disabled on entry
107 *
108 * @param[in] qtd The QTD to add
109 * @param[in] hcd The DWC HCD structure
110 @@ -931,8 +932,6 @@ int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t *
111 dwc_otg_hcd_t * hcd, dwc_otg_qh_t ** qh, int atomic_alloc)
112 {
113 int retval = 0;
114 - dwc_irqflags_t flags;
115 -
116 dwc_otg_hcd_urb_t *urb = qtd->urb;
117
118 /*
119 @@ -946,15 +945,12 @@ int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t *
120 goto done;
121 }
122 }
123 - DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
124 retval = dwc_otg_hcd_qh_add(hcd, *qh);
125 if (retval == 0) {
126 DWC_CIRCLEQ_INSERT_TAIL(&((*qh)->qtd_list), qtd,
127 qtd_list_entry);
128 qtd->qh = *qh;
129 }
130 - DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
131 -
132 done:
133
134 return retval;