summaryrefslogtreecommitdiff
path: root/drivers/usb/host/ehci-hcd.c
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2012-07-11 15:22:49 (GMT)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-07-16 23:56:47 (GMT)
commit3c273a056bf46167f0a1309c2ba72282a17d2541 (patch)
tree91938cb7d5b087567420384a7c4438eb1b7f0252 /drivers/usb/host/ehci-hcd.c
parent9d9387475af261949f61a5ec465e1f762d7be08a (diff)
downloadlinux-3c273a056bf46167f0a1309c2ba72282a17d2541.tar.xz
USB: EHCI: unlink multiple async QHs together
This patch (as1582) changes ehci-hcd's strategy for unlinking async QHs. Currently the driver never unlinks more than one QH at a time. This can be inefficient and cause unnecessary delays, since a QH cannot be reused while it is waiting to be unlinked. The new strategy unlinks all the waiting QHs at once. In practice the improvement won't be very big, because it's somewhat uncommon to have two or more QHs waiting to be unlinked at any time. But it does happen, and in any case, doing things this way makes more sense IMO. The change requires the async unlinking code to be refactored slightly. Now in addition to the routines for starting and ending an unlink, there are new routines for unlinking a single QH and starting an IAA cycle. This approach is needed because there are two separate paths for unlinking async QHs: When a transfer error occurs or an URB is cancelled, the QH must be unlinked right away; When a QH has been idle sufficiently long, it is unlinked to avoid consuming DMA bandwidth uselessly. In the first case we want the unlink to proceed as quickly as possible, whereas in the second case we can afford to batch several QHs together and unlink them all at once. Hence the division of labor. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/host/ehci-hcd.c')
-rw-r--r--drivers/usb/host/ehci-hcd.c35
1 files changed, 4 insertions, 31 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index f74ba27..86e8ee1 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -795,7 +795,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
/* guard against (alleged) silicon errata */
if (cmd & CMD_IAAD)
ehci_dbg(ehci, "IAA with IAAD still set?\n");
- if (ehci->async_unlink) {
+ if (ehci->async_iaa) {
COUNT(ehci->stats.iaa);
end_unlink_async(ehci);
} else
@@ -926,33 +926,6 @@ static int ehci_urb_enqueue (
}
}
-static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
-{
- /* failfast */
- if (ehci->rh_state < EHCI_RH_RUNNING && ehci->async_unlink)
- end_unlink_async(ehci);
-
- /* If the QH isn't linked then there's nothing we can do
- * unless we were called during a giveback, in which case
- * qh_completions() has to deal with it.
- */
- if (qh->qh_state != QH_STATE_LINKED) {
- if (qh->qh_state == QH_STATE_COMPLETING)
- qh->needs_rescan = 1;
- return;
- }
-
- /* defer till later if busy */
- if (ehci->async_unlink) {
- qh->qh_state = QH_STATE_UNLINK_WAIT;
- ehci->async_unlink_last->unlink_next = qh;
- ehci->async_unlink_last = qh;
-
- /* start IAA cycle */
- } else
- start_unlink_async (ehci, qh);
-}
-
/* remove from hardware lists
* completions normally happen asynchronously
*/
@@ -979,7 +952,7 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
switch (qh->qh_state) {
case QH_STATE_LINKED:
case QH_STATE_COMPLETING:
- unlink_async(ehci, qh);
+ start_unlink_async(ehci, qh);
break;
case QH_STATE_UNLINK:
case QH_STATE_UNLINK_WAIT:
@@ -1070,7 +1043,7 @@ rescan:
* may already be unlinked.
*/
if (tmp)
- unlink_async(ehci, qh);
+ start_unlink_async(ehci, qh);
/* FALL THROUGH */
case QH_STATE_UNLINK: /* wait for hw to finish? */
case QH_STATE_UNLINK_WAIT:
@@ -1133,7 +1106,7 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
* re-linking will call qh_refresh().
*/
if (eptype == USB_ENDPOINT_XFER_BULK)
- unlink_async(ehci, qh);
+ start_unlink_async(ehci, qh);
else
start_unlink_intr(ehci, qh);
}