]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/2.6.32.13/usb-ehci-defer-reclamation-of-sitds.patch
Linux 4.9.167
[thirdparty/kernel/stable-queue.git] / releases / 2.6.32.13 / usb-ehci-defer-reclamation-of-sitds.patch
1 From 0e5f231bc16ff9910882fa5b9d64d80e7691cfab Mon Sep 17 00:00:00 2001
2 From: Alan Stern <stern@rowland.harvard.edu>
3 Date: Thu, 8 Apr 2010 16:56:37 -0400
4 Subject: USB: EHCI: defer reclamation of siTDs
5
6 From: Alan Stern <stern@rowland.harvard.edu>
7
8 commit 0e5f231bc16ff9910882fa5b9d64d80e7691cfab upstream.
9
10 This patch (as1369) fixes a problem in ehci-hcd. Some controllers
11 occasionally run into trouble when the driver reclaims siTDs too
12 quickly. This can happen while streaming audio; it causes the
13 controller to crash.
14
15 The patch changes siTD reclamation to work the same way as iTD
16 reclamation: Completed siTDs are stored on a list and not reused until
17 at least one frame has passed.
18
19 Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
20 Tested-by: Nate Case <ncase@xes-inc.com>
21 Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
22
23 ---
24 drivers/usb/host/ehci-hcd.c | 1 +
25 drivers/usb/host/ehci-mem.c | 2 +-
26 drivers/usb/host/ehci-sched.c | 38 ++++++++++++++++++++++++++++++--------
27 drivers/usb/host/ehci.h | 5 +++--
28 4 files changed, 35 insertions(+), 11 deletions(-)
29
30 --- a/drivers/usb/host/ehci-hcd.c
31 +++ b/drivers/usb/host/ehci-hcd.c
32 @@ -543,6 +543,7 @@ static int ehci_init(struct usb_hcd *hcd
33 */
34 ehci->periodic_size = DEFAULT_I_TDPS;
35 INIT_LIST_HEAD(&ehci->cached_itd_list);
36 + INIT_LIST_HEAD(&ehci->cached_sitd_list);
37 if ((retval = ehci_mem_init(ehci, GFP_KERNEL)) < 0)
38 return retval;
39
40 --- a/drivers/usb/host/ehci-mem.c
41 +++ b/drivers/usb/host/ehci-mem.c
42 @@ -136,7 +136,7 @@ static inline void qh_put (struct ehci_q
43
44 static void ehci_mem_cleanup (struct ehci_hcd *ehci)
45 {
46 - free_cached_itd_list(ehci);
47 + free_cached_lists(ehci);
48 if (ehci->async)
49 qh_put (ehci->async);
50 ehci->async = NULL;
51 --- a/drivers/usb/host/ehci-sched.c
52 +++ b/drivers/usb/host/ehci-sched.c
53 @@ -2127,13 +2127,27 @@ sitd_complete (
54 (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
55 }
56 iso_stream_put (ehci, stream);
57 - /* OK to recycle this SITD now that its completion callback ran. */
58 +
59 done:
60 sitd->urb = NULL;
61 - sitd->stream = NULL;
62 - list_move(&sitd->sitd_list, &stream->free_list);
63 - iso_stream_put(ehci, stream);
64 -
65 + if (ehci->clock_frame != sitd->frame) {
66 + /* OK to recycle this SITD now. */
67 + sitd->stream = NULL;
68 + list_move(&sitd->sitd_list, &stream->free_list);
69 + iso_stream_put(ehci, stream);
70 + } else {
71 + /* HW might remember this SITD, so we can't recycle it yet.
72 + * Move it to a safe place until a new frame starts.
73 + */
74 + list_move(&sitd->sitd_list, &ehci->cached_sitd_list);
75 + if (stream->refcount == 2) {
76 + /* If iso_stream_put() were called here, stream
77 + * would be freed. Instead, just prevent reuse.
78 + */
79 + stream->ep->hcpriv = NULL;
80 + stream->ep = NULL;
81 + }
82 + }
83 return retval;
84 }
85
86 @@ -2199,9 +2213,10 @@ done:
87
88 /*-------------------------------------------------------------------------*/
89
90 -static void free_cached_itd_list(struct ehci_hcd *ehci)
91 +static void free_cached_lists(struct ehci_hcd *ehci)
92 {
93 struct ehci_itd *itd, *n;
94 + struct ehci_sitd *sitd, *sn;
95
96 list_for_each_entry_safe(itd, n, &ehci->cached_itd_list, itd_list) {
97 struct ehci_iso_stream *stream = itd->stream;
98 @@ -2209,6 +2224,13 @@ static void free_cached_itd_list(struct
99 list_move(&itd->itd_list, &stream->free_list);
100 iso_stream_put(ehci, stream);
101 }
102 +
103 + list_for_each_entry_safe(sitd, sn, &ehci->cached_sitd_list, sitd_list) {
104 + struct ehci_iso_stream *stream = sitd->stream;
105 + sitd->stream = NULL;
106 + list_move(&sitd->sitd_list, &stream->free_list);
107 + iso_stream_put(ehci, stream);
108 + }
109 }
110
111 /*-------------------------------------------------------------------------*/
112 @@ -2235,7 +2257,7 @@ scan_periodic (struct ehci_hcd *ehci)
113 clock_frame = -1;
114 }
115 if (ehci->clock_frame != clock_frame) {
116 - free_cached_itd_list(ehci);
117 + free_cached_lists(ehci);
118 ehci->clock_frame = clock_frame;
119 }
120 clock %= mod;
121 @@ -2398,7 +2420,7 @@ restart:
122 clock = now;
123 clock_frame = clock >> 3;
124 if (ehci->clock_frame != clock_frame) {
125 - free_cached_itd_list(ehci);
126 + free_cached_lists(ehci);
127 ehci->clock_frame = clock_frame;
128 }
129 } else {
130 --- a/drivers/usb/host/ehci.h
131 +++ b/drivers/usb/host/ehci.h
132 @@ -87,8 +87,9 @@ struct ehci_hcd { /* one per controlle
133 int next_uframe; /* scan periodic, start here */
134 unsigned periodic_sched; /* periodic activity count */
135
136 - /* list of itds completed while clock_frame was still active */
137 + /* list of itds & sitds completed while clock_frame was still active */
138 struct list_head cached_itd_list;
139 + struct list_head cached_sitd_list;
140 unsigned clock_frame;
141
142 /* per root hub port */
143 @@ -195,7 +196,7 @@ timer_action_done (struct ehci_hcd *ehci
144 clear_bit (action, &ehci->actions);
145 }
146
147 -static void free_cached_itd_list(struct ehci_hcd *ehci);
148 +static void free_cached_lists(struct ehci_hcd *ehci);
149
150 /*-------------------------------------------------------------------------*/
151