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

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: default avatarAlan Stern <stern@rowland.harvard.edu>
CC: David Brownell <david-b@pacbell.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 3a44494e
...@@ -934,8 +934,9 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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: ...@@ -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) { /* Tell the caller to start an unlink */
intr_deschedule (ehci, qh); qh->needs_rescan = 1;
(void) qh_schedule (ehci, qh);
} else {
/* Tell the caller to start an unlink */
qh->needs_rescan = 1;
}
break; break;
/* otherwise, unlink already started */ /* otherwise, unlink already started */
} }
......
...@@ -615,8 +615,19 @@ static int qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) ...@@ -615,8 +615,19 @@ static int qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) 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) ...@@ -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: ...@@ -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;
......
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