Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

USB: EHCI: use hrtimer for (s)iTD deallocation

This patch (as1579) adds an hrtimer event to handle deallocation of
iTDs and siTDs in ehci-hcd.

Because of the frame-oriented approach used by the EHCI periodic
schedule, the hardware can continue to access the Transfer Descriptor
for isochronous (or split-isochronous) transactions for up to a
millisecond after the transaction completes. The iTD (or siTD) must
not be reused before then.

The strategy currently used involves putting completed iTDs on a list
of cached entries and every so often returning them to the endpoint's
free list. The new strategy reduces overhead by putting completed
iTDs back on the free list immediately, although they are not reused
until it is safe to do so.

When the isochronous endpoint stops (its queue becomes empty), the
iTDs on its free list get moved to a global list, from which they will
be deallocated after a minimum of 2 ms. This delay is what the new
hrtimer event is for.

Overall this may not be a tremendous improvement over the current
code, but to me it seems a lot more clear and logical. In addition,
it removes the need for each iTD to keep a reference to the
ehci_iso_stream it belongs to, since the iTD never needs to be moved
back to the stream's free list from the global list.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Alan Stern and committed by
Greg Kroah-Hartman
55934eb3 bf6387bc

+90 -105
+1
drivers/usb/host/ehci-hcd.c
··· 509 509 spin_lock_irq (&ehci->lock); 510 510 if (ehci->async) 511 511 ehci_work (ehci); 512 + end_free_itds(ehci); 512 513 spin_unlock_irq (&ehci->lock); 513 514 ehci_mem_cleanup (ehci); 514 515
+1
drivers/usb/host/ehci-hub.c
··· 303 303 if (ehci->async_unlink) 304 304 end_unlink_async(ehci); 305 305 ehci_handle_intr_unlinks(ehci); 306 + end_free_itds(ehci); 306 307 307 308 /* allow remote wakeup */ 308 309 mask = INTR_MASK;
-1
drivers/usb/host/ehci-mem.c
··· 118 118 119 119 static void ehci_mem_cleanup (struct ehci_hcd *ehci) 120 120 { 121 - free_cached_lists(ehci); 122 121 if (ehci->async) 123 122 qh_destroy(ehci, ehci->async); 124 123 ehci->async = NULL;
+39 -98
drivers/usb/host/ehci-sched.c
··· 1045 1045 if (stream->refcount == 1) { 1046 1046 // BUG_ON (!list_empty(&stream->td_list)); 1047 1047 1048 - while (!list_empty (&stream->free_list)) { 1049 - struct list_head *entry; 1050 - 1051 - entry = stream->free_list.next; 1052 - list_del (entry); 1053 - 1054 - /* knows about ITD vs SITD */ 1055 - if (stream->highspeed) { 1056 - struct ehci_itd *itd; 1057 - 1058 - itd = list_entry (entry, struct ehci_itd, 1059 - itd_list); 1060 - dma_pool_free (ehci->itd_pool, itd, 1061 - itd->itd_dma); 1062 - } else { 1063 - struct ehci_sitd *sitd; 1064 - 1065 - sitd = list_entry (entry, struct ehci_sitd, 1066 - sitd_list); 1067 - dma_pool_free (ehci->sitd_pool, sitd, 1068 - sitd->sitd_dma); 1069 - } 1070 - } 1071 - 1072 - stream->bEndpointAddress &= 0x0f; 1073 1048 if (stream->ep) 1074 1049 stream->ep->hcpriv = NULL; 1075 1050 ··· 1205 1230 spin_lock_irqsave (&ehci->lock, flags); 1206 1231 for (i = 0; i < num_itds; i++) { 1207 1232 1208 - /* free_list.next might be cache-hot ... but maybe 1209 - * the HC caches it too. avoid that issue for now. 1233 + /* 1234 + * Use iTDs from the free list, but not iTDs that may 1235 + * still be in use by the hardware. 1210 1236 */ 1211 - 1212 - /* prefer previously-allocated itds */ 1213 - if (likely (!list_empty(&stream->free_list))) { 1214 - itd = list_entry (stream->free_list.prev, 1237 + if (likely(!list_empty(&stream->free_list))) { 1238 + itd = list_first_entry(&stream->free_list, 1215 1239 struct ehci_itd, itd_list); 1240 + if (itd->frame == ehci->clock_frame) 1241 + goto alloc_itd; 1216 1242 list_del (&itd->itd_list); 1217 1243 itd_dma = itd->itd_dma; 1218 1244 } else { 1245 + alloc_itd: 1219 1246 spin_unlock_irqrestore (&ehci->lock, flags); 1220 1247 itd = dma_pool_alloc (ehci->itd_pool, mem_flags, 1221 1248 &itd_dma); ··· 1739 1762 1740 1763 done: 1741 1764 itd->urb = NULL; 1742 - if (ehci->clock_frame != itd->frame || itd->index[7] != -1) { 1743 - /* OK to recycle this ITD now. */ 1744 - itd->stream = NULL; 1745 - list_move(&itd->itd_list, &stream->free_list); 1746 - iso_stream_put(ehci, stream); 1747 - } else { 1748 - /* HW might remember this ITD, so we can't recycle it yet. 1749 - * Move it to a safe place until a new frame starts. 1750 - */ 1751 - list_move(&itd->itd_list, &ehci->cached_itd_list); 1752 - if (stream->refcount == 2) { 1753 - /* If iso_stream_put() were called here, stream 1754 - * would be freed. Instead, just prevent reuse. 1755 - */ 1756 - stream->ep->hcpriv = NULL; 1757 - stream->ep = NULL; 1758 - } 1765 + 1766 + /* Add to the end of the free list for later reuse */ 1767 + list_move_tail(&itd->itd_list, &stream->free_list); 1768 + 1769 + /* Recycle the iTDs when the pipeline is empty (ep no longer in use) */ 1770 + if (list_empty(&stream->td_list)) { 1771 + list_splice_tail_init(&stream->free_list, 1772 + &ehci->cached_itd_list); 1773 + start_free_itds(ehci); 1759 1774 } 1775 + 1776 + iso_stream_put(ehci, stream); 1760 1777 return retval; 1761 1778 } 1762 1779 ··· 1901 1930 * means we never need two sitds for full speed packets. 1902 1931 */ 1903 1932 1904 - /* free_list.next might be cache-hot ... but maybe 1905 - * the HC caches it too. avoid that issue for now. 1933 + /* 1934 + * Use siTDs from the free list, but not siTDs that may 1935 + * still be in use by the hardware. 1906 1936 */ 1907 - 1908 - /* prefer previously-allocated sitds */ 1909 - if (!list_empty(&stream->free_list)) { 1910 - sitd = list_entry (stream->free_list.prev, 1937 + if (likely(!list_empty(&stream->free_list))) { 1938 + sitd = list_first_entry(&stream->free_list, 1911 1939 struct ehci_sitd, sitd_list); 1940 + if (sitd->frame == ehci->clock_frame) 1941 + goto alloc_sitd; 1912 1942 list_del (&sitd->sitd_list); 1913 1943 sitd_dma = sitd->sitd_dma; 1914 1944 } else { 1945 + alloc_sitd: 1915 1946 spin_unlock_irqrestore (&ehci->lock, flags); 1916 1947 sitd = dma_pool_alloc (ehci->sitd_pool, mem_flags, 1917 1948 &sitd_dma); ··· 2130 2157 2131 2158 done: 2132 2159 sitd->urb = NULL; 2133 - if (ehci->clock_frame != sitd->frame) { 2134 - /* OK to recycle this SITD now. */ 2135 - sitd->stream = NULL; 2136 - list_move(&sitd->sitd_list, &stream->free_list); 2137 - iso_stream_put(ehci, stream); 2138 - } else { 2139 - /* HW might remember this SITD, so we can't recycle it yet. 2140 - * Move it to a safe place until a new frame starts. 2141 - */ 2142 - list_move(&sitd->sitd_list, &ehci->cached_sitd_list); 2143 - if (stream->refcount == 2) { 2144 - /* If iso_stream_put() were called here, stream 2145 - * would be freed. Instead, just prevent reuse. 2146 - */ 2147 - stream->ep->hcpriv = NULL; 2148 - stream->ep = NULL; 2149 - } 2160 + 2161 + /* Add to the end of the free list for later reuse */ 2162 + list_move_tail(&sitd->sitd_list, &stream->free_list); 2163 + 2164 + /* Recycle the siTDs when the pipeline is empty (ep no longer in use) */ 2165 + if (list_empty(&stream->td_list)) { 2166 + list_splice_tail_init(&stream->free_list, 2167 + &ehci->cached_sitd_list); 2168 + start_free_itds(ehci); 2150 2169 } 2170 + 2171 + iso_stream_put(ehci, stream); 2151 2172 return retval; 2152 2173 } 2153 2174 ··· 2206 2239 2207 2240 /*-------------------------------------------------------------------------*/ 2208 2241 2209 - static void free_cached_lists(struct ehci_hcd *ehci) 2210 - { 2211 - struct ehci_itd *itd, *n; 2212 - struct ehci_sitd *sitd, *sn; 2213 - 2214 - list_for_each_entry_safe(itd, n, &ehci->cached_itd_list, itd_list) { 2215 - struct ehci_iso_stream *stream = itd->stream; 2216 - itd->stream = NULL; 2217 - list_move(&itd->itd_list, &stream->free_list); 2218 - iso_stream_put(ehci, stream); 2219 - } 2220 - 2221 - list_for_each_entry_safe(sitd, sn, &ehci->cached_sitd_list, sitd_list) { 2222 - struct ehci_iso_stream *stream = sitd->stream; 2223 - sitd->stream = NULL; 2224 - list_move(&sitd->sitd_list, &stream->free_list); 2225 - iso_stream_put(ehci, stream); 2226 - } 2227 - } 2228 - 2229 - /*-------------------------------------------------------------------------*/ 2230 - 2231 2242 static void 2232 2243 scan_periodic (struct ehci_hcd *ehci) 2233 2244 { ··· 2227 2282 clock = now_uframe + mod - 1; 2228 2283 clock_frame = -1; 2229 2284 } 2230 - if (ehci->clock_frame != clock_frame) { 2231 - free_cached_lists(ehci); 2232 - ehci->clock_frame = clock_frame; 2233 - } 2285 + ehci->clock_frame = clock_frame; 2234 2286 clock &= mod - 1; 2235 2287 clock_frame = clock >> 3; 2236 2288 ++ehci->periodic_stamp; ··· 2405 2463 clock = now; 2406 2464 clock_frame = clock >> 3; 2407 2465 if (ehci->clock_frame != clock_frame) { 2408 - free_cached_lists(ehci); 2409 2466 ehci->clock_frame = clock_frame; 2410 2467 ++ehci->periodic_stamp; 2411 2468 }
+46 -4
drivers/usb/host/ehci-timer.c
··· 71 71 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_PSS */ 72 72 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_DEAD */ 73 73 1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */ 74 + 2 * NSEC_PER_MSEC, /* EHCI_HRTIMER_FREE_ITDS */ 74 75 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 75 76 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */ 76 77 }; ··· 166 165 167 166 /* The status is up-to-date; restart or stop the schedule as needed */ 168 167 if (want == 0) { /* Stopped */ 169 - free_cached_lists(ehci); 170 168 if (ehci->periodic_count > 0) { 171 169 172 170 /* make sure ehci_work scans these */ ··· 188 188 static void ehci_disable_PSE(struct ehci_hcd *ehci) 189 189 { 190 190 ehci_clear_command_bit(ehci, CMD_PSE); 191 - 192 - /* Poll to see when it actually stops */ 193 - ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true); 194 191 } 195 192 196 193 ··· 247 250 } 248 251 249 252 253 + /* Start another free-iTDs/siTDs cycle */ 254 + static void start_free_itds(struct ehci_hcd *ehci) 255 + { 256 + if (!(ehci->enabled_hrtimer_events & BIT(EHCI_HRTIMER_FREE_ITDS))) { 257 + ehci->last_itd_to_free = list_entry( 258 + ehci->cached_itd_list.prev, 259 + struct ehci_itd, itd_list); 260 + ehci->last_sitd_to_free = list_entry( 261 + ehci->cached_sitd_list.prev, 262 + struct ehci_sitd, sitd_list); 263 + ehci_enable_event(ehci, EHCI_HRTIMER_FREE_ITDS, true); 264 + } 265 + } 266 + 267 + /* Wait for controller to stop using old iTDs and siTDs */ 268 + static void end_free_itds(struct ehci_hcd *ehci) 269 + { 270 + struct ehci_itd *itd, *n; 271 + struct ehci_sitd *sitd, *sn; 272 + 273 + if (ehci->rh_state < EHCI_RH_RUNNING) { 274 + ehci->last_itd_to_free = NULL; 275 + ehci->last_sitd_to_free = NULL; 276 + } 277 + 278 + list_for_each_entry_safe(itd, n, &ehci->cached_itd_list, itd_list) { 279 + list_del(&itd->itd_list); 280 + dma_pool_free(ehci->itd_pool, itd, itd->itd_dma); 281 + if (itd == ehci->last_itd_to_free) 282 + break; 283 + } 284 + list_for_each_entry_safe(sitd, sn, &ehci->cached_sitd_list, sitd_list) { 285 + list_del(&sitd->sitd_list); 286 + dma_pool_free(ehci->sitd_pool, sitd, sitd->sitd_dma); 287 + if (sitd == ehci->last_sitd_to_free) 288 + break; 289 + } 290 + 291 + if (!list_empty(&ehci->cached_itd_list) || 292 + !list_empty(&ehci->cached_sitd_list)) 293 + start_free_itds(ehci); 294 + } 295 + 296 + 250 297 /* 251 298 * Handler functions for the hrtimer event types. 252 299 * Keep this array in the same order as the event types indexed by ··· 301 260 ehci_poll_PSS, /* EHCI_HRTIMER_POLL_PSS */ 302 261 ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */ 303 262 ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */ 263 + end_free_itds, /* EHCI_HRTIMER_FREE_ITDS */ 304 264 ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 305 265 ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */ 306 266 };
+3 -2
drivers/usb/host/ehci.h
··· 83 83 EHCI_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */ 84 84 EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ 85 85 EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ 86 + EHCI_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */ 86 87 EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ 87 88 EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ 88 89 EHCI_HRTIMER_NUM_EVENTS /* Must come last */ ··· 140 139 141 140 /* list of itds & sitds completed while clock_frame was still active */ 142 141 struct list_head cached_itd_list; 142 + struct ehci_itd *last_itd_to_free; 143 143 struct list_head cached_sitd_list; 144 + struct ehci_sitd *last_sitd_to_free; 144 145 unsigned clock_frame; 145 146 146 147 /* per root hub port */ ··· 252 249 { 253 250 clear_bit (action, &ehci->actions); 254 251 } 255 - 256 - static void free_cached_lists(struct ehci_hcd *ehci); 257 252 258 253 /*-------------------------------------------------------------------------*/ 259 254