Commit 7bc782d7 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

USB: EHCI: split needs_rescan into two flags

This patch (as1662) does some more QH-related cleanup in ehci-hcd.
The qh->needs_rescan flag is currently used for two different
purposes; the patch replaces it with two separate flags for greater
clarity: qh->dequeue_during_giveback indicates that a completion
handler dequeued an URB (implying that a rescan is needed), and
qh->exception indicates that the QH is in an exceptional state
requiring an unlink (either it encountered an I/O error or an unlink
was requested).

The new flags get set where the dequeue, exception, or unlink request
occurred, rather than where the unlink is started.  This is so that in
the future, if we need to, we will be able to tell apart unlinks that
truly were required from those that were carried out merely because
the QH wasn't being used.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 79bcf7b0
...@@ -903,11 +903,14 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) ...@@ -903,11 +903,14 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
qh = (struct ehci_qh *) urb->hcpriv; qh = (struct ehci_qh *) urb->hcpriv;
if (!qh) if (!qh)
break; break;
qh->exception = 1;
switch (qh->qh_state) { switch (qh->qh_state) {
case QH_STATE_LINKED: case QH_STATE_LINKED:
case QH_STATE_COMPLETING:
start_unlink_async(ehci, qh); start_unlink_async(ehci, qh);
break; break;
case QH_STATE_COMPLETING:
qh->dequeue_during_giveback = 1;
break;
case QH_STATE_UNLINK: case QH_STATE_UNLINK:
case QH_STATE_UNLINK_WAIT: case QH_STATE_UNLINK_WAIT:
/* already started */ /* already started */
...@@ -923,11 +926,14 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) ...@@ -923,11 +926,14 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
qh = (struct ehci_qh *) urb->hcpriv; qh = (struct ehci_qh *) urb->hcpriv;
if (!qh) if (!qh)
break; break;
qh->exception = 1;
switch (qh->qh_state) { switch (qh->qh_state) {
case QH_STATE_LINKED: case QH_STATE_LINKED:
case QH_STATE_COMPLETING:
start_unlink_intr(ehci, qh); start_unlink_intr(ehci, qh);
break; break;
case QH_STATE_COMPLETING:
qh->dequeue_during_giveback = 1;
break;
case QH_STATE_IDLE: case QH_STATE_IDLE:
qh_completions (ehci, qh); qh_completions (ehci, qh);
break; break;
...@@ -984,6 +990,7 @@ ehci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep) ...@@ -984,6 +990,7 @@ ehci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep)
goto done; goto done;
} }
qh->exception = 1;
if (ehci->rh_state < EHCI_RH_RUNNING) if (ehci->rh_state < EHCI_RH_RUNNING)
qh->qh_state = QH_STATE_IDLE; qh->qh_state = QH_STATE_IDLE;
switch (qh->qh_state) { switch (qh->qh_state) {
...@@ -1052,13 +1059,12 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) ...@@ -1052,13 +1059,12 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
usb_settoggle(qh->dev, epnum, is_out, 0); usb_settoggle(qh->dev, epnum, is_out, 0);
if (!list_empty(&qh->qtd_list)) { if (!list_empty(&qh->qtd_list)) {
WARN_ONCE(1, "clear_halt for a busy endpoint\n"); WARN_ONCE(1, "clear_halt for a busy endpoint\n");
} else if (qh->qh_state == QH_STATE_LINKED || } else {
qh->qh_state == QH_STATE_COMPLETING) {
/* The toggle value in the QH can't be updated /* The toggle value in the QH can't be updated
* while the QH is active. Unlink it now; * while the QH is active. Unlink it now;
* re-linking will call qh_refresh(). * re-linking will call qh_refresh().
*/ */
qh->exception = 1;
if (eptype == USB_ENDPOINT_XFER_BULK) if (eptype == USB_ENDPOINT_XFER_BULK)
start_unlink_async(ehci, qh); start_unlink_async(ehci, qh);
else else
......
...@@ -322,7 +322,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) ...@@ -322,7 +322,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
rescan: rescan:
last = NULL; last = NULL;
last_status = -EINPROGRESS; last_status = -EINPROGRESS;
qh->needs_rescan = 0; qh->dequeue_during_giveback = 0;
/* remove de-activated QTDs from front of queue. /* remove de-activated QTDs from front of queue.
* after faults (including short reads), cleanup this urb * after faults (including short reads), cleanup this urb
...@@ -518,18 +518,12 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) ...@@ -518,18 +518,12 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
} }
/* Do we need to rescan for URBs dequeued during a giveback? */ /* Do we need to rescan for URBs dequeued during a giveback? */
if (unlikely(qh->needs_rescan)) { if (unlikely(qh->dequeue_during_giveback)) {
/* If the QH is already unlinked, do the rescan now. */ /* If the QH is already unlinked, do the rescan now. */
if (state == QH_STATE_IDLE) if (state == QH_STATE_IDLE)
goto rescan; goto rescan;
/* Otherwise we have to wait until the QH is fully unlinked. /* Otherwise the caller must unlink the QH. */
* Our caller will start an unlink if qh->needs_rescan is
* set. But if an unlink has already started, nothing needs
* to be done.
*/
if (state != QH_STATE_LINKED)
qh->needs_rescan = 0;
} }
/* restore original state; caller must unlink or relink */ /* restore original state; caller must unlink or relink */
...@@ -538,29 +532,23 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) ...@@ -538,29 +532,23 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
/* be sure the hardware's done with the qh before refreshing /* be sure the hardware's done with the qh before refreshing
* it after fault cleanup, or recovering from silicon wrongly * it after fault cleanup, or recovering from silicon wrongly
* overlaying the dummy qtd (which reduces DMA chatter). * overlaying the dummy qtd (which reduces DMA chatter).
*
* 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;
* - HC reads rest of that QH, including token
* Result: HC gets an inconsistent image, and then
* DMAs to/from the wrong memory (corrupting it).
*
* That should be rare for interrupt transfers,
* except maybe high bandwidth ...
*/ */
if (stopped != 0 || hw->hw_qtd_next == EHCI_LIST_END(ehci)) { if (stopped != 0 || hw->hw_qtd_next == EHCI_LIST_END(ehci))
if (state == QH_STATE_LINKED) { qh->exception = 1;
/*
* 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;
* - HC reads rest of that QH, including token
* Result: HC gets an inconsistent image, and then
* DMAs to/from the wrong memory (corrupting it).
*
* That should be rare for interrupt transfers,
* except maybe high bandwidth ...
*
* Therefore tell the caller to start an unlink.
*/
qh->needs_rescan = 1;
}
/* otherwise, unlink already started */
}
return qh->needs_rescan; /* Let the caller know if the QH needs to be unlinked. */
return qh->exception;
} }
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -1002,8 +990,9 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) ...@@ -1002,8 +990,9 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
head->qh_next.qh = qh; head->qh_next.qh = qh;
head->hw->hw_next = dma; head->hw->hw_next = dma;
qh->xacterrs = 0;
qh->qh_state = QH_STATE_LINKED; qh->qh_state = QH_STATE_LINKED;
qh->xacterrs = 0;
qh->exception = 0;
/* qtd completions reported later by interrupt */ /* qtd completions reported later by interrupt */
enable_async(ehci); enable_async(ehci);
...@@ -1317,16 +1306,9 @@ static void unlink_empty_async_suspended(struct ehci_hcd *ehci) ...@@ -1317,16 +1306,9 @@ static void unlink_empty_async_suspended(struct ehci_hcd *ehci)
static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh) static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh)
{ {
/* /* If the QH isn't linked then there's nothing we can do. */
* If the QH isn't linked then there's nothing we can do if (qh->qh_state != QH_STATE_LINKED)
* 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; return;
}
single_unlink_async(ehci, qh); single_unlink_async(ehci, qh);
start_iaa_cycle(ehci, false); start_iaa_cycle(ehci, false);
......
...@@ -539,6 +539,7 @@ static void qh_link_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) ...@@ -539,6 +539,7 @@ static void qh_link_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
} }
qh->qh_state = QH_STATE_LINKED; qh->qh_state = QH_STATE_LINKED;
qh->xacterrs = 0; qh->xacterrs = 0;
qh->exception = 0;
/* update per-qh bandwidth for usbfs */ /* update per-qh bandwidth for usbfs */
ehci_to_hcd(ehci)->self.bandwidth_allocated += qh->period ehci_to_hcd(ehci)->self.bandwidth_allocated += qh->period
...@@ -602,15 +603,9 @@ static void qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) ...@@ -602,15 +603,9 @@ static void qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh)
{ {
/* If the QH isn't linked then there's nothing we can do /* If the QH isn't linked then there's nothing we can do. */
* unless we were called during a giveback, in which case if (qh->qh_state != QH_STATE_LINKED)
* 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; return;
}
qh_unlink_periodic (ehci, qh); qh_unlink_periodic (ehci, qh);
......
...@@ -384,7 +384,6 @@ struct ehci_qh { ...@@ -384,7 +384,6 @@ struct ehci_qh {
unsigned unlink_cycle; unsigned unlink_cycle;
u8 needs_rescan; /* Dequeue during giveback */
u8 qh_state; u8 qh_state;
#define QH_STATE_LINKED 1 /* HC sees this */ #define QH_STATE_LINKED 1 /* HC sees this */
#define QH_STATE_UNLINK 2 /* HC may still see this */ #define QH_STATE_UNLINK 2 /* HC may still see this */
...@@ -407,6 +406,9 @@ struct ehci_qh { ...@@ -407,6 +406,9 @@ struct ehci_qh {
struct usb_device *dev; /* access to TT */ struct usb_device *dev; /* access to TT */
unsigned is_out:1; /* bulk or intr OUT */ unsigned is_out:1; /* bulk or intr OUT */
unsigned clearing_tt:1; /* Clear-TT-Buf in progress */ unsigned clearing_tt:1; /* Clear-TT-Buf in progress */
unsigned dequeue_during_giveback:1;
unsigned exception:1; /* got a fault, or an unlink
was requested */
}; };
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment