aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host
diff options
context:
space:
mode:
authorMing Lei <ming.lei@canonical.com>2013-07-03 10:53:10 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-08-12 14:43:48 -0400
commit9118f9eb4f1e97a135de3f78853c411befcf9775 (patch)
tree1db640132f3a1dfbffbbe74ca2382c3239de8d90 /drivers/usb/host
parent35371e4fbc3e1863a6e7a79b8c17c25cc96a1380 (diff)
USB: EHCI: improve interrupt qh unlink
ehci-hcd currently unlinks an interrupt QH when it becomes empty, that is, after its last URB completes. This works well because in almost all cases, the completion handler for an interrupt URB resubmits the URB; therefore the QH doesn't become empty and doesn't get unlinked. When we start using tasklets for URB completion, this scheme won't work as well. The resubmission won't occur until the tasklet runs, which will be some time after the completion is queued with the tasklet. During that delay, the QH will be empty and so will be unlinked unnecessarily. To prevent this problem, this patch adds a 5-ms time delay before empty interrupt QHs are unlinked. Most often, during that time the interrupt URB will be resubmitted and thus we can avoid unlinking the QH. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Ming Lei <ming.lei@canonical.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/ehci-hcd.c1
-rw-r--r--drivers/usb/host/ehci-hub.c1
-rw-r--r--drivers/usb/host/ehci-mem.c1
-rw-r--r--drivers/usb/host/ehci-sched.c47
-rw-r--r--drivers/usb/host/ehci-timer.c34
-rw-r--r--drivers/usb/host/ehci.h3
6 files changed, 84 insertions, 3 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 387cedf2b698..7ad9ef8aedee 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -487,6 +487,7 @@ static int ehci_init(struct usb_hcd *hcd)
487 ehci->periodic_size = DEFAULT_I_TDPS; 487 ehci->periodic_size = DEFAULT_I_TDPS;
488 INIT_LIST_HEAD(&ehci->async_unlink); 488 INIT_LIST_HEAD(&ehci->async_unlink);
489 INIT_LIST_HEAD(&ehci->async_idle); 489 INIT_LIST_HEAD(&ehci->async_idle);
490 INIT_LIST_HEAD(&ehci->intr_unlink_wait);
490 INIT_LIST_HEAD(&ehci->intr_unlink); 491 INIT_LIST_HEAD(&ehci->intr_unlink);
491 INIT_LIST_HEAD(&ehci->intr_qh_list); 492 INIT_LIST_HEAD(&ehci->intr_qh_list);
492 INIT_LIST_HEAD(&ehci->cached_itd_list); 493 INIT_LIST_HEAD(&ehci->cached_itd_list);
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 6e69ee1a3371..269a2e8ad0ab 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -345,6 +345,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
345 345
346 end_unlink_async(ehci); 346 end_unlink_async(ehci);
347 unlink_empty_async_suspended(ehci); 347 unlink_empty_async_suspended(ehci);
348 ehci_handle_start_intr_unlinks(ehci);
348 ehci_handle_intr_unlinks(ehci); 349 ehci_handle_intr_unlinks(ehci);
349 end_free_itds(ehci); 350 end_free_itds(ehci);
350 351
diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c
index ef2c3a1eca4b..52a77734a225 100644
--- a/drivers/usb/host/ehci-mem.c
+++ b/drivers/usb/host/ehci-mem.c
@@ -93,6 +93,7 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags)
93 qh->qh_dma = dma; 93 qh->qh_dma = dma;
94 // INIT_LIST_HEAD (&qh->qh_list); 94 // INIT_LIST_HEAD (&qh->qh_list);
95 INIT_LIST_HEAD (&qh->qtd_list); 95 INIT_LIST_HEAD (&qh->qtd_list);
96 INIT_LIST_HEAD(&qh->unlink_node);
96 97
97 /* dummy td enables safe urb queuing */ 98 /* dummy td enables safe urb queuing */
98 qh->dummy = ehci_qtd_alloc (ehci, flags); 99 qh->dummy = ehci_qtd_alloc (ehci, flags);
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index f80d0330d548..94388738a6f7 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -601,12 +601,29 @@ static void qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
601 list_del(&qh->intr_node); 601 list_del(&qh->intr_node);
602} 602}
603 603
604static void cancel_unlink_wait_intr(struct ehci_hcd *ehci, struct ehci_qh *qh)
605{
606 if (qh->qh_state != QH_STATE_LINKED ||
607 list_empty(&qh->unlink_node))
608 return;
609
610 list_del_init(&qh->unlink_node);
611
612 /*
613 * TODO: disable the event of EHCI_HRTIMER_START_UNLINK_INTR for
614 * avoiding unnecessary CPU wakeup
615 */
616}
617
604static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) 618static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh)
605{ 619{
606 /* If the QH isn't linked then there's nothing we can do. */ 620 /* If the QH isn't linked then there's nothing we can do. */
607 if (qh->qh_state != QH_STATE_LINKED) 621 if (qh->qh_state != QH_STATE_LINKED)
608 return; 622 return;
609 623
624 /* if the qh is waiting for unlink, cancel it now */
625 cancel_unlink_wait_intr(ehci, qh);
626
610 qh_unlink_periodic (ehci, qh); 627 qh_unlink_periodic (ehci, qh);
611 628
612 /* Make sure the unlinks are visible before starting the timer */ 629 /* Make sure the unlinks are visible before starting the timer */
@@ -632,6 +649,27 @@ static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh)
632 } 649 }
633} 650}
634 651
652/*
653 * It is common only one intr URB is scheduled on one qh, and
654 * given complete() is run in tasklet context, introduce a bit
655 * delay to avoid unlink qh too early.
656 */
657static void start_unlink_intr_wait(struct ehci_hcd *ehci,
658 struct ehci_qh *qh)
659{
660 qh->unlink_cycle = ehci->intr_unlink_wait_cycle;
661
662 /* New entries go at the end of the intr_unlink_wait list */
663 list_add_tail(&qh->unlink_node, &ehci->intr_unlink_wait);
664
665 if (ehci->rh_state < EHCI_RH_RUNNING)
666 ehci_handle_start_intr_unlinks(ehci);
667 else if (ehci->intr_unlink_wait.next == &qh->unlink_node) {
668 ehci_enable_event(ehci, EHCI_HRTIMER_START_UNLINK_INTR, true);
669 ++ehci->intr_unlink_wait_cycle;
670 }
671}
672
635static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) 673static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh)
636{ 674{
637 struct ehci_qh_hw *hw = qh->hw; 675 struct ehci_qh_hw *hw = qh->hw;
@@ -889,6 +927,9 @@ static int intr_submit (
889 if (qh->qh_state == QH_STATE_IDLE) { 927 if (qh->qh_state == QH_STATE_IDLE) {
890 qh_refresh(ehci, qh); 928 qh_refresh(ehci, qh);
891 qh_link_periodic(ehci, qh); 929 qh_link_periodic(ehci, qh);
930 } else {
931 /* cancel unlink wait for the qh */
932 cancel_unlink_wait_intr(ehci, qh);
892 } 933 }
893 934
894 /* ... update usbfs periodic stats */ 935 /* ... update usbfs periodic stats */
@@ -924,9 +965,11 @@ static void scan_intr(struct ehci_hcd *ehci)
924 * in qh_unlink_periodic(). 965 * in qh_unlink_periodic().
925 */ 966 */
926 temp = qh_completions(ehci, qh); 967 temp = qh_completions(ehci, qh);
927 if (unlikely(temp || (list_empty(&qh->qtd_list) && 968 if (unlikely(temp))
928 qh->qh_state == QH_STATE_LINKED)))
929 start_unlink_intr(ehci, qh); 969 start_unlink_intr(ehci, qh);
970 else if (unlikely(list_empty(&qh->qtd_list) &&
971 qh->qh_state == QH_STATE_LINKED))
972 start_unlink_intr_wait(ehci, qh);
930 } 973 }
931 } 974 }
932} 975}
diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c
index 11e5b32f73e9..424ac5d83714 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 5 * NSEC_PER_MSEC, /* EHCI_HRTIMER_START_UNLINK_INTR */
75 6 * NSEC_PER_MSEC, /* EHCI_HRTIMER_ASYNC_UNLINKS */ 76 6 * NSEC_PER_MSEC, /* EHCI_HRTIMER_ASYNC_UNLINKS */
76 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_IAA_WATCHDOG */ 77 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_IAA_WATCHDOG */
77 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 78 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */
@@ -215,6 +216,36 @@ static void ehci_handle_controller_death(struct ehci_hcd *ehci)
215 /* Not in process context, so don't try to reset the controller */ 216 /* Not in process context, so don't try to reset the controller */
216} 217}
217 218
219/* start to unlink interrupt QHs */
220static void ehci_handle_start_intr_unlinks(struct ehci_hcd *ehci)
221{
222 bool stopped = (ehci->rh_state < EHCI_RH_RUNNING);
223
224 /*
225 * Process all the QHs on the intr_unlink list that were added
226 * before the current unlink cycle began. The list is in
227 * temporal order, so stop when we reach the first entry in the
228 * current cycle. But if the root hub isn't running then
229 * process all the QHs on the list.
230 */
231 while (!list_empty(&ehci->intr_unlink_wait)) {
232 struct ehci_qh *qh;
233
234 qh = list_first_entry(&ehci->intr_unlink_wait,
235 struct ehci_qh, unlink_node);
236 if (!stopped && (qh->unlink_cycle ==
237 ehci->intr_unlink_wait_cycle))
238 break;
239 list_del_init(&qh->unlink_node);
240 start_unlink_intr(ehci, qh);
241 }
242
243 /* Handle remaining entries later */
244 if (!list_empty(&ehci->intr_unlink_wait)) {
245 ehci_enable_event(ehci, EHCI_HRTIMER_START_UNLINK_INTR, true);
246 ++ehci->intr_unlink_wait_cycle;
247 }
248}
218 249
219/* Handle unlinked interrupt QHs once they are gone from the hardware */ 250/* Handle unlinked interrupt QHs once they are gone from the hardware */
220static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci) 251static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci)
@@ -236,7 +267,7 @@ static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci)
236 unlink_node); 267 unlink_node);
237 if (!stopped && qh->unlink_cycle == ehci->intr_unlink_cycle) 268 if (!stopped && qh->unlink_cycle == ehci->intr_unlink_cycle)
238 break; 269 break;
239 list_del(&qh->unlink_node); 270 list_del_init(&qh->unlink_node);
240 end_unlink_intr(ehci, qh); 271 end_unlink_intr(ehci, qh);
241 } 272 }
242 273
@@ -363,6 +394,7 @@ static void (*event_handlers[])(struct ehci_hcd *) = {
363 ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */ 394 ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */
364 ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */ 395 ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */
365 end_free_itds, /* EHCI_HRTIMER_FREE_ITDS */ 396 end_free_itds, /* EHCI_HRTIMER_FREE_ITDS */
397 ehci_handle_start_intr_unlinks, /* EHCI_HRTIMER_START_UNLINK_INTR */
366 unlink_empty_async, /* EHCI_HRTIMER_ASYNC_UNLINKS */ 398 unlink_empty_async, /* EHCI_HRTIMER_ASYNC_UNLINKS */
367 ehci_iaa_watchdog, /* EHCI_HRTIMER_IAA_WATCHDOG */ 399 ehci_iaa_watchdog, /* EHCI_HRTIMER_IAA_WATCHDOG */
368 ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 400 ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 64f9a08e959c..947752015d5d 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -88,6 +88,7 @@ enum ehci_hrtimer_event {
88 EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ 88 EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */
89 EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ 89 EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */
90 EHCI_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */ 90 EHCI_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */
91 EHCI_HRTIMER_START_UNLINK_INTR, /* Unlink empty interrupt QHs */
91 EHCI_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */ 92 EHCI_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */
92 EHCI_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */ 93 EHCI_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */
93 EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ 94 EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */
@@ -143,7 +144,9 @@ struct ehci_hcd { /* one per controller */
143 unsigned i_thresh; /* uframes HC might cache */ 144 unsigned i_thresh; /* uframes HC might cache */
144 145
145 union ehci_shadow *pshadow; /* mirror hw periodic table */ 146 union ehci_shadow *pshadow; /* mirror hw periodic table */
147 struct list_head intr_unlink_wait;
146 struct list_head intr_unlink; 148 struct list_head intr_unlink;
149 unsigned intr_unlink_wait_cycle;
147 unsigned intr_unlink_cycle; 150 unsigned intr_unlink_cycle;
148 unsigned now_frame; /* frame from HC hardware */ 151 unsigned now_frame; /* frame from HC hardware */
149 unsigned last_iso_frame; /* last frame scanned for iso */ 152 unsigned last_iso_frame; /* last frame scanned for iso */