aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2012-07-11 11:22:53 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-07-16 19:56:47 -0400
commit32830f207691176234b4c4dd17f0d7ab6d87d94b (patch)
tree4b352a92dfc720418288c541d87e93594f97f859 /drivers/usb
parent3c273a056bf46167f0a1309c2ba72282a17d2541 (diff)
USB: EHCI: use hrtimer for unlinking empty async QHs
This patch (as1583) changes ehci-hcd to use an hrtimer event for unlinking empty (unused) async QHs instead of using a kernel timer. The check for empty QHs is moved to a new routine, where it doesn't require going through an entire scan of both the async and periodic schedules. And it can unlink multiple QHs at once, unlike the current code. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/host/ehci-hcd.c16
-rw-r--r--drivers/usb/host/ehci-hub.c1
-rw-r--r--drivers/usb/host/ehci-q.c78
-rw-r--r--drivers/usb/host/ehci-timer.c2
-rw-r--r--drivers/usb/host/ehci.h4
5 files changed, 59 insertions, 42 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 86e8ee169c67..74ffd20edff8 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -94,8 +94,6 @@ static const char hcd_name [] = "ehci_hcd";
94#define EHCI_TUNE_FLS 1 /* (medium) 512-frame schedule */ 94#define EHCI_TUNE_FLS 1 /* (medium) 512-frame schedule */
95 95
96#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ 96#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
97#define EHCI_SHRINK_JIFFIES (DIV_ROUND_UP(HZ, 200) + 1)
98 /* 5-ms async qh unlink delay */
99 97
100/* Initial IRQ latency: faster than hw default */ 98/* Initial IRQ latency: faster than hw default */
101static int log2_irq_thresh = 0; // 0 to 6 99static int log2_irq_thresh = 0; // 0 to 6
@@ -130,15 +128,6 @@ MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us");
130static void 128static void
131timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action) 129timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action)
132{ 130{
133 /* Don't override timeouts which shrink or (later) disable
134 * the async ring; just the I/O watchdog. Note that if a
135 * SHRINK were pending, OFF would never be requested.
136 */
137 if (timer_pending(&ehci->watchdog)
138 && (BIT(TIMER_ASYNC_SHRINK)
139 & ehci->actions))
140 return;
141
142 if (!test_and_set_bit(action, &ehci->actions)) { 131 if (!test_and_set_bit(action, &ehci->actions)) {
143 unsigned long t; 132 unsigned long t;
144 133
@@ -148,10 +137,6 @@ timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action)
148 return; 137 return;
149 t = EHCI_IO_JIFFIES; 138 t = EHCI_IO_JIFFIES;
150 break; 139 break;
151 /* case TIMER_ASYNC_SHRINK: */
152 default:
153 t = EHCI_SHRINK_JIFFIES;
154 break;
155 } 140 }
156 mod_timer(&ehci->watchdog, t + jiffies); 141 mod_timer(&ehci->watchdog, t + jiffies);
157 } 142 }
@@ -307,6 +292,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci)
307/*-------------------------------------------------------------------------*/ 292/*-------------------------------------------------------------------------*/
308 293
309static void end_unlink_async(struct ehci_hcd *ehci); 294static void end_unlink_async(struct ehci_hcd *ehci);
295static void unlink_empty_async(struct ehci_hcd *ehci);
310static void ehci_work(struct ehci_hcd *ehci); 296static void ehci_work(struct ehci_hcd *ehci);
311static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh); 297static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh);
312static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh); 298static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh);
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index a3822700e496..5d84562e2716 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -300,6 +300,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
300 ehci->rh_state = EHCI_RH_SUSPENDED; 300 ehci->rh_state = EHCI_RH_SUSPENDED;
301 301
302 end_unlink_async(ehci); 302 end_unlink_async(ehci);
303 unlink_empty_async(ehci);
303 ehci_handle_intr_unlinks(ehci); 304 ehci_handle_intr_unlinks(ehci);
304 end_free_itds(ehci); 305 end_free_itds(ehci);
305 306
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 181832921c53..bae931767825 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -1205,7 +1205,7 @@ static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested)
1205 end_unlink_async(ehci); 1205 end_unlink_async(ehci);
1206 1206
1207 /* Otherwise start a new IAA cycle */ 1207 /* Otherwise start a new IAA cycle */
1208 } else { 1208 } else if (likely(ehci->rh_state == EHCI_RH_RUNNING)) {
1209 /* Make sure the unlinks are all visible to the hardware */ 1209 /* Make sure the unlinks are all visible to the hardware */
1210 wmb(); 1210 wmb();
1211 1211
@@ -1253,6 +1253,39 @@ static void end_unlink_async(struct ehci_hcd *ehci)
1253 } 1253 }
1254} 1254}
1255 1255
1256static void unlink_empty_async(struct ehci_hcd *ehci)
1257{
1258 struct ehci_qh *qh, *next;
1259 bool stopped = (ehci->rh_state < EHCI_RH_RUNNING);
1260 bool check_unlinks_later = false;
1261
1262 /* Unlink all the async QHs that have been empty for a timer cycle */
1263 next = ehci->async->qh_next.qh;
1264 while (next) {
1265 qh = next;
1266 next = qh->qh_next.qh;
1267
1268 if (list_empty(&qh->qtd_list) &&
1269 qh->qh_state == QH_STATE_LINKED) {
1270 if (!stopped && qh->unlink_cycle ==
1271 ehci->async_unlink_cycle)
1272 check_unlinks_later = true;
1273 else
1274 single_unlink_async(ehci, qh);
1275 }
1276 }
1277
1278 /* Start a new IAA cycle if any QHs are waiting for it */
1279 if (ehci->async_unlink)
1280 start_iaa_cycle(ehci, false);
1281
1282 /* QHs that haven't been empty for long enough will be handled later */
1283 if (check_unlinks_later) {
1284 ehci_enable_event(ehci, EHCI_HRTIMER_ASYNC_UNLINKS, true);
1285 ++ehci->async_unlink_cycle;
1286 }
1287}
1288
1256/* makes sure the async qh will become idle */ 1289/* makes sure the async qh will become idle */
1257/* caller must own ehci->lock */ 1290/* caller must own ehci->lock */
1258 1291
@@ -1277,12 +1310,8 @@ static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh)
1277 1310
1278static void scan_async (struct ehci_hcd *ehci) 1311static void scan_async (struct ehci_hcd *ehci)
1279{ 1312{
1280 bool stopped;
1281 struct ehci_qh *qh; 1313 struct ehci_qh *qh;
1282 enum ehci_timer_action action = TIMER_IO_WATCHDOG; 1314 bool check_unlinks_later = false;
1283
1284 timer_action_done (ehci, TIMER_ASYNC_SHRINK);
1285 stopped = (ehci->rh_state < EHCI_RH_RUNNING);
1286 1315
1287 ehci->qh_scan_next = ehci->async->qh_next.qh; 1316 ehci->qh_scan_next = ehci->async->qh_next.qh;
1288 while (ehci->qh_scan_next) { 1317 while (ehci->qh_scan_next) {
@@ -1301,28 +1330,27 @@ static void scan_async (struct ehci_hcd *ehci)
1301 * in single_unlink_async(). 1330 * in single_unlink_async().
1302 */ 1331 */
1303 temp = qh_completions(ehci, qh); 1332 temp = qh_completions(ehci, qh);
1304 if (qh->needs_rescan) 1333 if (qh->needs_rescan) {
1305 start_unlink_async(ehci, qh); 1334 start_unlink_async(ehci, qh);
1306 qh->unlink_time = jiffies + EHCI_SHRINK_JIFFIES; 1335 } else if (list_empty(&qh->qtd_list)
1307 if (temp != 0) 1336 && qh->qh_state == QH_STATE_LINKED) {
1337 qh->unlink_cycle = ehci->async_unlink_cycle;
1338 check_unlinks_later = true;
1339 } else if (temp != 0)
1308 goto rescan; 1340 goto rescan;
1309 } 1341 }
1342 }
1310 1343
1311 /* unlink idle entries, reducing DMA usage as well 1344 /*
1312 * as HCD schedule-scanning costs. delay for any qh 1345 * Unlink empty entries, reducing DMA usage as well
1313 * we just scanned, there's a not-unusual case that it 1346 * as HCD schedule-scanning costs. Delay for any qh
1314 * doesn't stay idle for long. 1347 * we just scanned, there's a not-unusual case that it
1315 * (plus, avoids some kind of re-activation race.) 1348 * doesn't stay idle for long.
1316 */ 1349 */
1317 if (list_empty(&qh->qtd_list) 1350 if (check_unlinks_later && ehci->rh_state == EHCI_RH_RUNNING &&
1318 && qh->qh_state == QH_STATE_LINKED) { 1351 !(ehci->enabled_hrtimer_events &
1319 if (!ehci->async_unlink && (stopped || 1352 BIT(EHCI_HRTIMER_ASYNC_UNLINKS))) {
1320 time_after_eq(jiffies, qh->unlink_time))) 1353 ehci_enable_event(ehci, EHCI_HRTIMER_ASYNC_UNLINKS, true);
1321 start_unlink_async(ehci, qh); 1354 ++ehci->async_unlink_cycle;
1322 else
1323 action = TIMER_ASYNC_SHRINK;
1324 }
1325 } 1355 }
1326 if (action == TIMER_ASYNC_SHRINK)
1327 timer_action (ehci, TIMER_ASYNC_SHRINK);
1328} 1356}
diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c
index 8ca5f152f5bd..a823290b5139 100644
--- a/drivers/usb/host/ehci-timer.c
+++ b/drivers/usb/host/ehci-timer.c
@@ -72,6 +72,7 @@ static unsigned event_delays_ns[] = {
72 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_DEAD */ 72 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_DEAD */
73 1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */ 73 1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */
74 2 * NSEC_PER_MSEC, /* EHCI_HRTIMER_FREE_ITDS */ 74 2 * NSEC_PER_MSEC, /* EHCI_HRTIMER_FREE_ITDS */
75 6 * NSEC_PER_MSEC, /* EHCI_HRTIMER_ASYNC_UNLINKS */
75 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_IAA_WATCHDOG */ 76 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_IAA_WATCHDOG */
76 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 77 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */
77 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */ 78 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */
@@ -347,6 +348,7 @@ static void (*event_handlers[])(struct ehci_hcd *) = {
347 ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */ 348 ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */
348 ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */ 349 ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */
349 end_free_itds, /* EHCI_HRTIMER_FREE_ITDS */ 350 end_free_itds, /* EHCI_HRTIMER_FREE_ITDS */
351 unlink_empty_async, /* EHCI_HRTIMER_ASYNC_UNLINKS */
350 ehci_iaa_watchdog, /* EHCI_HRTIMER_IAA_WATCHDOG */ 352 ehci_iaa_watchdog, /* EHCI_HRTIMER_IAA_WATCHDOG */
351 ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 353 ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */
352 ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */ 354 ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 1a782775881b..303c36cc99c9 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -84,6 +84,7 @@ enum ehci_hrtimer_event {
84 EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ 84 EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */
85 EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ 85 EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */
86 EHCI_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */ 86 EHCI_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */
87 EHCI_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */
87 EHCI_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */ 88 EHCI_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */
88 EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ 89 EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */
89 EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ 90 EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */
@@ -123,6 +124,7 @@ struct ehci_hcd { /* one per controller */
123 struct ehci_qh *async_unlink_last; 124 struct ehci_qh *async_unlink_last;
124 struct ehci_qh *async_iaa; 125 struct ehci_qh *async_iaa;
125 struct ehci_qh *qh_scan_next; 126 struct ehci_qh *qh_scan_next;
127 unsigned async_unlink_cycle;
126 unsigned async_count; /* async activity count */ 128 unsigned async_count; /* async activity count */
127 129
128 /* periodic schedule support */ 130 /* periodic schedule support */
@@ -232,7 +234,6 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci)
232 234
233enum ehci_timer_action { 235enum ehci_timer_action {
234 TIMER_IO_WATCHDOG, 236 TIMER_IO_WATCHDOG,
235 TIMER_ASYNC_SHRINK,
236}; 237};
237 238
238static inline void 239static inline void
@@ -382,7 +383,6 @@ struct ehci_qh {
382 struct ehci_qtd *dummy; 383 struct ehci_qtd *dummy;
383 struct ehci_qh *unlink_next; /* next on unlink list */ 384 struct ehci_qh *unlink_next; /* next on unlink list */
384 385
385 unsigned long unlink_time;
386 unsigned unlink_cycle; 386 unsigned unlink_cycle;
387 unsigned stamp; 387 unsigned stamp;
388 388