diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2013-03-22 18:30:43 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-03-25 21:35:05 +0100 |
commit | c1fdb68e3d73741630ca16695cf9176c233be7ed (patch) | |
tree | b256bd0c02c458730537f6d4b5b470adbce7f97b /drivers | |
parent | USB: usb-skeleton.c: fix blocked forever in skel_read (diff) | |
download | linux-c1fdb68e3d73741630ca16695cf9176c233be7ed.tar.xz linux-c1fdb68e3d73741630ca16695cf9176c233be7ed.zip |
USB: EHCI: changes related to qh_refresh()
This patch (as1638) makes several changes to the ehci-hcd driver, all
related to the qh_refresh() function. This function must be called
whenever an idle QH gets linked back into either the async or the
periodic schedule.
Change a BUG_ON() in the qh_update routine to a WARN_ON().
Since this code runs in atomic context, a BUG_ON() would
immediately freeze the whole system.
Remove two unneeded calls to qh_refresh(), one when a QH is
initialized and one when a QH becomes idle. Adjust the
adjacent comments accordingly.
Move the qh_refresh() and qh_link_periodic() calls for new
interrupt URBs to after the new TDs have been added.
As a result of the previous two changes, qh_refresh() is never
called when the qtd_list is empty. The corresponding check in
qh_refresh() can be removed, along with an indentation level.
These changes should not cause any alteration of behavior.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/host/ehci-q.c | 52 | ||||
-rw-r--r-- | drivers/usb/host/ehci-sched.c | 9 |
2 files changed, 26 insertions, 35 deletions
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 23d136904285..b824cb674898 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -90,7 +90,7 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd) struct ehci_qh_hw *hw = qh->hw; /* writes to an active overlay are unsafe */ - BUG_ON(qh->qh_state != QH_STATE_IDLE); + WARN_ON(qh->qh_state != QH_STATE_IDLE); hw->hw_qtd_next = QTD_NEXT(ehci, qtd->qtd_dma); hw->hw_alt_next = EHCI_LIST_END(ehci); @@ -123,26 +123,19 @@ qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh) { struct ehci_qtd *qtd; - if (list_empty (&qh->qtd_list)) - qtd = qh->dummy; - else { - qtd = list_entry (qh->qtd_list.next, - struct ehci_qtd, qtd_list); - /* - * first qtd may already be partially processed. - * If we come here during unlink, the QH overlay region - * might have reference to the just unlinked qtd. The - * qtd is updated in qh_completions(). Update the QH - * overlay here. - */ - if (qh->hw->hw_token & ACTIVE_BIT(ehci)) { - qh->hw->hw_qtd_next = qtd->hw_next; - qtd = NULL; - } - } + qtd = list_entry(qh->qtd_list.next, struct ehci_qtd, qtd_list); - if (qtd) - qh_update (ehci, qh, qtd); + /* + * first qtd may already be partially processed. + * If we come here during unlink, the QH overlay region + * might have reference to the just unlinked qtd. The + * qtd is updated in qh_completions(). Update the QH + * overlay here. + */ + if (qh->hw->hw_token & ACTIVE_BIT(ehci)) + qh->hw->hw_qtd_next = qtd->hw_next; + else + qh_update(ehci, qh, qtd); } /*-------------------------------------------------------------------------*/ @@ -553,12 +546,9 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) * overlaying the dummy qtd (which reduces DMA chatter). */ if (stopped != 0 || hw->hw_qtd_next == EHCI_LIST_END(ehci)) { - switch (state) { - case QH_STATE_IDLE: - qh_refresh(ehci, qh); - break; - case QH_STATE_LINKED: - /* We won't refresh a QH that's linked (after the HC + if (state == QH_STATE_LINKED) { + /* + * We won't refresh a QH that's linked (after the HC * stopped the queue). That avoids a race: * - HC reads first part of QH; * - CPU updates that first part and the token; @@ -568,13 +558,12 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) * * That should be rare for interrupt transfers, * except maybe high bandwidth ... + * + * Therefore tell the caller to start an unlink. */ - - /* Tell the caller to start an unlink */ qh->needs_rescan = 1; - break; - /* otherwise, unlink already started */ } + /* otherwise, unlink already started */ } return count; @@ -957,14 +946,13 @@ done: /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */ - /* init as live, toggle clear, advance to dummy */ + /* init as live, toggle clear */ qh->qh_state = QH_STATE_IDLE; hw = qh->hw; hw->hw_info1 = cpu_to_hc32(ehci, info1); hw->hw_info2 = cpu_to_hc32(ehci, info2); qh->is_out = !is_input; usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1); - qh_refresh (ehci, qh); return qh; } diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index b476daf49f6f..66259dc7822e 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -792,7 +792,6 @@ static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh) unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ struct ehci_qh_hw *hw = qh->hw; - qh_refresh(ehci, qh); hw->hw_next = EHCI_LIST_END(ehci); frame = qh->start; @@ -844,8 +843,6 @@ static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh) } else ehci_dbg (ehci, "reused qh %p schedule\n", qh); - /* stuff into the periodic schedule */ - qh_link_periodic(ehci, qh); done: return status; } @@ -891,6 +888,12 @@ static int intr_submit ( qh = qh_append_tds(ehci, urb, qtd_list, epnum, &urb->ep->hcpriv); BUG_ON (qh == NULL); + /* stuff into the periodic schedule */ + if (qh->qh_state == QH_STATE_IDLE) { + qh_refresh(ehci, qh); + qh_link_periodic(ehci, qh); + } + /* ... update usbfs periodic stats */ ehci_to_hcd(ehci)->self.bandwidth_int_reqs++; |