mirror of
https://github.com/adulau/aha.git
synced 2024-12-31 21:26:18 +00:00
USB: EHCI: change deschedule logic for interrupt QHs
This patch (as1281) changes the way ehci-hcd deschedules interrupt QHs, copying the approach used for async QHs. The caller is no longer responsible for rescheduling the QH if its queue is non-empty; instead the reschedule is done directly by intr_deschedule(), after calling qh_completions(). This is exactly the same as how end_unlink_async() works. ehci_urb_dequeue() and intr_deschedule() now correctly handle the case where they are called while another interrupt URB for the same QH is being given back. This was a surprisingly large blind spot. And scan_periodic() now respects the new needs_rescan flag. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> CC: David Brownell <david-b@pacbell.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
3a44494e23
commit
a448c9d8c5
3 changed files with 40 additions and 34 deletions
|
@ -934,8 +934,9 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||||
break;
|
break;
|
||||||
switch (qh->qh_state) {
|
switch (qh->qh_state) {
|
||||||
case QH_STATE_LINKED:
|
case QH_STATE_LINKED:
|
||||||
|
case QH_STATE_COMPLETING:
|
||||||
intr_deschedule (ehci, qh);
|
intr_deschedule (ehci, qh);
|
||||||
/* FALL THROUGH */
|
break;
|
||||||
case QH_STATE_IDLE:
|
case QH_STATE_IDLE:
|
||||||
qh_completions (ehci, qh);
|
qh_completions (ehci, qh);
|
||||||
break;
|
break;
|
||||||
|
@ -944,23 +945,6 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||||
qh, qh->qh_state);
|
qh, qh->qh_state);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* reschedule QH iff another request is queued */
|
|
||||||
if (!list_empty (&qh->qtd_list)
|
|
||||||
&& HC_IS_RUNNING (hcd->state)) {
|
|
||||||
rc = qh_schedule(ehci, qh);
|
|
||||||
|
|
||||||
/* An error here likely indicates handshake failure
|
|
||||||
* or no space left in the schedule. Neither fault
|
|
||||||
* should happen often ...
|
|
||||||
*
|
|
||||||
* FIXME kill the now-dysfunctional queued urbs
|
|
||||||
*/
|
|
||||||
if (rc != 0)
|
|
||||||
ehci_err(ehci,
|
|
||||||
"can't reschedule qh %p, err %d",
|
|
||||||
qh, rc);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PIPE_ISOCHRONOUS:
|
case PIPE_ISOCHRONOUS:
|
||||||
|
@ -1079,12 +1063,10 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
|
||||||
* 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().
|
||||||
*/
|
*/
|
||||||
if (eptype == USB_ENDPOINT_XFER_BULK) {
|
if (eptype == USB_ENDPOINT_XFER_BULK)
|
||||||
unlink_async(ehci, qh);
|
unlink_async(ehci, qh);
|
||||||
} else {
|
else
|
||||||
intr_deschedule(ehci, qh);
|
intr_deschedule(ehci, qh);
|
||||||
(void) qh_schedule(ehci, qh);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&ehci->lock, flags);
|
spin_unlock_irqrestore(&ehci->lock, flags);
|
||||||
|
|
|
@ -299,7 +299,6 @@ __acquires(ehci->lock)
|
||||||
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);
|
||||||
static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh);
|
static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh);
|
||||||
|
|
||||||
static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh);
|
|
||||||
static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh);
|
static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -555,14 +554,9 @@ halt:
|
||||||
* That should be rare for interrupt transfers,
|
* That should be rare for interrupt transfers,
|
||||||
* except maybe high bandwidth ...
|
* except maybe high bandwidth ...
|
||||||
*/
|
*/
|
||||||
if ((cpu_to_hc32(ehci, QH_SMASK)
|
|
||||||
& hw->hw_info2) != 0) {
|
|
||||||
intr_deschedule (ehci, qh);
|
|
||||||
(void) qh_schedule (ehci, qh);
|
|
||||||
} else {
|
|
||||||
/* Tell the caller to start an unlink */
|
/* Tell the caller to start an unlink */
|
||||||
qh->needs_rescan = 1;
|
qh->needs_rescan = 1;
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
/* otherwise, unlink already started */
|
/* otherwise, unlink already started */
|
||||||
}
|
}
|
||||||
|
|
|
@ -617,6 +617,17 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||||
{
|
{
|
||||||
unsigned wait;
|
unsigned wait;
|
||||||
struct ehci_qh_hw *hw = qh->hw;
|
struct ehci_qh_hw *hw = qh->hw;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
qh_unlink_periodic (ehci, qh);
|
qh_unlink_periodic (ehci, qh);
|
||||||
|
|
||||||
|
@ -636,6 +647,24 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
||||||
qh->qh_state = QH_STATE_IDLE;
|
qh->qh_state = QH_STATE_IDLE;
|
||||||
hw->hw_next = EHCI_LIST_END(ehci);
|
hw->hw_next = EHCI_LIST_END(ehci);
|
||||||
wmb ();
|
wmb ();
|
||||||
|
|
||||||
|
qh_completions(ehci, qh);
|
||||||
|
|
||||||
|
/* reschedule QH iff another request is queued */
|
||||||
|
if (!list_empty(&qh->qtd_list) &&
|
||||||
|
HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) {
|
||||||
|
rc = qh_schedule(ehci, qh);
|
||||||
|
|
||||||
|
/* An error here likely indicates handshake failure
|
||||||
|
* or no space left in the schedule. Neither fault
|
||||||
|
* should happen often ...
|
||||||
|
*
|
||||||
|
* FIXME kill the now-dysfunctional queued urbs
|
||||||
|
*/
|
||||||
|
if (rc != 0)
|
||||||
|
ehci_err(ehci, "can't reschedule qh %p, err %d\n",
|
||||||
|
qh, rc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
@ -2213,7 +2242,8 @@ restart:
|
||||||
type = Q_NEXT_TYPE(ehci, q.qh->hw->hw_next);
|
type = Q_NEXT_TYPE(ehci, q.qh->hw->hw_next);
|
||||||
q = q.qh->qh_next;
|
q = q.qh->qh_next;
|
||||||
modified = qh_completions (ehci, temp.qh);
|
modified = qh_completions (ehci, temp.qh);
|
||||||
if (unlikely (list_empty (&temp.qh->qtd_list)))
|
if (unlikely(list_empty(&temp.qh->qtd_list) ||
|
||||||
|
temp.qh->needs_rescan))
|
||||||
intr_deschedule (ehci, temp.qh);
|
intr_deschedule (ehci, temp.qh);
|
||||||
qh_put (temp.qh);
|
qh_put (temp.qh);
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in a new issue