diff options
| -rw-r--r-- | drivers/usb/host/ehci-hcd.c | 1 | ||||
| -rw-r--r-- | drivers/usb/host/ehci-mem.c | 2 | ||||
| -rw-r--r-- | drivers/usb/host/ehci-sched.c | 40 | ||||
| -rw-r--r-- | drivers/usb/host/ehci.h | 5 |
4 files changed, 36 insertions, 12 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 207e7a85aeb0..13ead00aecd5 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c | |||
| @@ -543,6 +543,7 @@ static int ehci_init(struct usb_hcd *hcd) | |||
| 543 | */ | 543 | */ |
| 544 | ehci->periodic_size = DEFAULT_I_TDPS; | 544 | ehci->periodic_size = DEFAULT_I_TDPS; |
| 545 | INIT_LIST_HEAD(&ehci->cached_itd_list); | 545 | INIT_LIST_HEAD(&ehci->cached_itd_list); |
| 546 | INIT_LIST_HEAD(&ehci->cached_sitd_list); | ||
| 546 | if ((retval = ehci_mem_init(ehci, GFP_KERNEL)) < 0) | 547 | if ((retval = ehci_mem_init(ehci, GFP_KERNEL)) < 0) |
| 547 | return retval; | 548 | return retval; |
| 548 | 549 | ||
diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c index aeda96e0af67..1f3f01eacaf0 100644 --- a/drivers/usb/host/ehci-mem.c +++ b/drivers/usb/host/ehci-mem.c | |||
| @@ -136,7 +136,7 @@ static inline void qh_put (struct ehci_qh *qh) | |||
| 136 | 136 | ||
| 137 | static void ehci_mem_cleanup (struct ehci_hcd *ehci) | 137 | static void ehci_mem_cleanup (struct ehci_hcd *ehci) |
| 138 | { | 138 | { |
| 139 | free_cached_itd_list(ehci); | 139 | free_cached_lists(ehci); |
| 140 | if (ehci->async) | 140 | if (ehci->async) |
| 141 | qh_put (ehci->async); | 141 | qh_put (ehci->async); |
| 142 | ehci->async = NULL; | 142 | ehci->async = NULL; |
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index a0aaaaff2560..805ec633a652 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c | |||
| @@ -510,7 +510,7 @@ static int disable_periodic (struct ehci_hcd *ehci) | |||
| 510 | ehci_writel(ehci, cmd, &ehci->regs->command); | 510 | ehci_writel(ehci, cmd, &ehci->regs->command); |
| 511 | /* posted write ... */ | 511 | /* posted write ... */ |
| 512 | 512 | ||
| 513 | free_cached_itd_list(ehci); | 513 | free_cached_lists(ehci); |
| 514 | 514 | ||
| 515 | ehci->next_uframe = -1; | 515 | ehci->next_uframe = -1; |
| 516 | return 0; | 516 | return 0; |
| @@ -2139,13 +2139,27 @@ sitd_complete ( | |||
| 2139 | (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); | 2139 | (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); |
| 2140 | } | 2140 | } |
| 2141 | iso_stream_put (ehci, stream); | 2141 | iso_stream_put (ehci, stream); |
| 2142 | /* OK to recycle this SITD now that its completion callback ran. */ | 2142 | |
| 2143 | done: | 2143 | done: |
| 2144 | sitd->urb = NULL; | 2144 | sitd->urb = NULL; |
| 2145 | sitd->stream = NULL; | 2145 | if (ehci->clock_frame != sitd->frame) { |
| 2146 | list_move(&sitd->sitd_list, &stream->free_list); | 2146 | /* OK to recycle this SITD now. */ |
| 2147 | iso_stream_put(ehci, stream); | 2147 | sitd->stream = NULL; |
| 2148 | 2148 | list_move(&sitd->sitd_list, &stream->free_list); | |
| 2149 | iso_stream_put(ehci, stream); | ||
| 2150 | } else { | ||
| 2151 | /* HW might remember this SITD, so we can't recycle it yet. | ||
| 2152 | * Move it to a safe place until a new frame starts. | ||
| 2153 | */ | ||
| 2154 | list_move(&sitd->sitd_list, &ehci->cached_sitd_list); | ||
| 2155 | if (stream->refcount == 2) { | ||
| 2156 | /* If iso_stream_put() were called here, stream | ||
| 2157 | * would be freed. Instead, just prevent reuse. | ||
| 2158 | */ | ||
| 2159 | stream->ep->hcpriv = NULL; | ||
| 2160 | stream->ep = NULL; | ||
| 2161 | } | ||
| 2162 | } | ||
| 2149 | return retval; | 2163 | return retval; |
| 2150 | } | 2164 | } |
| 2151 | 2165 | ||
| @@ -2211,9 +2225,10 @@ done: | |||
| 2211 | 2225 | ||
| 2212 | /*-------------------------------------------------------------------------*/ | 2226 | /*-------------------------------------------------------------------------*/ |
| 2213 | 2227 | ||
| 2214 | static void free_cached_itd_list(struct ehci_hcd *ehci) | 2228 | static void free_cached_lists(struct ehci_hcd *ehci) |
| 2215 | { | 2229 | { |
| 2216 | struct ehci_itd *itd, *n; | 2230 | struct ehci_itd *itd, *n; |
| 2231 | struct ehci_sitd *sitd, *sn; | ||
| 2217 | 2232 | ||
| 2218 | list_for_each_entry_safe(itd, n, &ehci->cached_itd_list, itd_list) { | 2233 | list_for_each_entry_safe(itd, n, &ehci->cached_itd_list, itd_list) { |
| 2219 | struct ehci_iso_stream *stream = itd->stream; | 2234 | struct ehci_iso_stream *stream = itd->stream; |
| @@ -2221,6 +2236,13 @@ static void free_cached_itd_list(struct ehci_hcd *ehci) | |||
| 2221 | list_move(&itd->itd_list, &stream->free_list); | 2236 | list_move(&itd->itd_list, &stream->free_list); |
| 2222 | iso_stream_put(ehci, stream); | 2237 | iso_stream_put(ehci, stream); |
| 2223 | } | 2238 | } |
| 2239 | |||
| 2240 | list_for_each_entry_safe(sitd, sn, &ehci->cached_sitd_list, sitd_list) { | ||
| 2241 | struct ehci_iso_stream *stream = sitd->stream; | ||
| 2242 | sitd->stream = NULL; | ||
| 2243 | list_move(&sitd->sitd_list, &stream->free_list); | ||
| 2244 | iso_stream_put(ehci, stream); | ||
| 2245 | } | ||
| 2224 | } | 2246 | } |
| 2225 | 2247 | ||
| 2226 | /*-------------------------------------------------------------------------*/ | 2248 | /*-------------------------------------------------------------------------*/ |
| @@ -2247,7 +2269,7 @@ scan_periodic (struct ehci_hcd *ehci) | |||
| 2247 | clock_frame = -1; | 2269 | clock_frame = -1; |
| 2248 | } | 2270 | } |
| 2249 | if (ehci->clock_frame != clock_frame) { | 2271 | if (ehci->clock_frame != clock_frame) { |
| 2250 | free_cached_itd_list(ehci); | 2272 | free_cached_lists(ehci); |
| 2251 | ehci->clock_frame = clock_frame; | 2273 | ehci->clock_frame = clock_frame; |
| 2252 | } | 2274 | } |
| 2253 | clock %= mod; | 2275 | clock %= mod; |
| @@ -2414,7 +2436,7 @@ restart: | |||
| 2414 | clock = now; | 2436 | clock = now; |
| 2415 | clock_frame = clock >> 3; | 2437 | clock_frame = clock >> 3; |
| 2416 | if (ehci->clock_frame != clock_frame) { | 2438 | if (ehci->clock_frame != clock_frame) { |
| 2417 | free_cached_itd_list(ehci); | 2439 | free_cached_lists(ehci); |
| 2418 | ehci->clock_frame = clock_frame; | 2440 | ehci->clock_frame = clock_frame; |
| 2419 | } | 2441 | } |
| 2420 | } else { | 2442 | } else { |
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index b1dce96dd621..556c0b48f3ab 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h | |||
| @@ -87,8 +87,9 @@ struct ehci_hcd { /* one per controller */ | |||
| 87 | int next_uframe; /* scan periodic, start here */ | 87 | int next_uframe; /* scan periodic, start here */ |
| 88 | unsigned periodic_sched; /* periodic activity count */ | 88 | unsigned periodic_sched; /* periodic activity count */ |
| 89 | 89 | ||
| 90 | /* list of itds completed while clock_frame was still active */ | 90 | /* list of itds & sitds completed while clock_frame was still active */ |
| 91 | struct list_head cached_itd_list; | 91 | struct list_head cached_itd_list; |
| 92 | struct list_head cached_sitd_list; | ||
| 92 | unsigned clock_frame; | 93 | unsigned clock_frame; |
| 93 | 94 | ||
| 94 | /* per root hub port */ | 95 | /* per root hub port */ |
| @@ -195,7 +196,7 @@ timer_action_done (struct ehci_hcd *ehci, enum ehci_timer_action action) | |||
| 195 | clear_bit (action, &ehci->actions); | 196 | clear_bit (action, &ehci->actions); |
| 196 | } | 197 | } |
| 197 | 198 | ||
| 198 | static void free_cached_itd_list(struct ehci_hcd *ehci); | 199 | static void free_cached_lists(struct ehci_hcd *ehci); |
| 199 | 200 | ||
| 200 | /*-------------------------------------------------------------------------*/ | 201 | /*-------------------------------------------------------------------------*/ |
| 201 | 202 | ||
