5cfc919344947837e3b8bb3e686024230d582498
[15.05/openwrt.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;