Commit 961c380c authored by David Brownell's avatar David Brownell Committed by Nathan Scott

[PATCH] OHCI urb unlink fixes

Fix two OHCI unlink issues.

   * All EDs now get a 1 msec delay before re-linking, even those
     which were seemingly "clean" unlink cases.  This gets rid of
     some list corruption issues ("bad entry") by getting rid of
     a fast-path carried over from 2.4 usb-ohci.

   * In case of unlink-during-submit, we must giveback() right away.
     This is a reasonably rare case.

There have been recent reports of problems here.  The "bad entry"
showed up with usbtest tests #11 and #12, or "stir4200", and maybe
in other cases.  The unlink-during-submit shows up in usbtest.
parent 72c03218
...@@ -229,11 +229,21 @@ static int ohci_urb_enqueue ( ...@@ -229,11 +229,21 @@ static int ohci_urb_enqueue (
goto fail; goto fail;
} }
/* in case of unlink-during-submit */
spin_lock (&urb->lock);
if (urb->status != -EINPROGRESS) {
spin_unlock (&urb->lock);
finish_urb (ohci, urb, 0);
retval = 0;
goto fail;
}
/* schedule the ed if needed */ /* schedule the ed if needed */
if (ed->state == ED_IDLE) { if (ed->state == ED_IDLE) {
retval = ed_schedule (ohci, ed); retval = ed_schedule (ohci, ed);
if (retval < 0) if (retval < 0)
goto fail; goto fail0;
if (ed->type == PIPE_ISOCHRONOUS) { if (ed->type == PIPE_ISOCHRONOUS) {
u16 frame = OHCI_FRAME_NO(ohci->hcca); u16 frame = OHCI_FRAME_NO(ohci->hcca);
...@@ -257,6 +267,8 @@ static int ohci_urb_enqueue ( ...@@ -257,6 +267,8 @@ static int ohci_urb_enqueue (
urb->hcpriv = urb_priv; urb->hcpriv = urb_priv;
td_submit_urb (ohci, urb); td_submit_urb (ohci, urb);
fail0:
spin_unlock (&urb->lock);
fail: fail:
if (retval) if (retval)
urb_free_priv (ohci, urb_priv); urb_free_priv (ohci, urb_priv);
......
...@@ -331,19 +331,6 @@ static void ed_deschedule (struct ohci_hcd *ohci, struct ed *ed) ...@@ -331,19 +331,6 @@ static void ed_deschedule (struct ohci_hcd *ohci, struct ed *ed)
periodic_unlink (ohci, ed); periodic_unlink (ohci, ed);
break; break;
} }
/* NOTE: Except for a couple of exceptionally clean unlink cases
* (like unlinking the only c/b ED, with no TDs) HCs may still be
* caching this operational ED (or its address). Safe unlinking
* involves not marking it ED_IDLE till INTR_SF; we always do that
* if td_list isn't empty. Otherwise the race is small; but ...
*/
if (ed->state == ED_OPER) {
ed->state = ED_IDLE;
ed->hwINFO &= ~(ED_SKIP | ED_DEQUEUE);
ed->hwHeadP &= ~ED_H;
wmb ();
}
} }
...@@ -665,6 +652,7 @@ static void td_submit_urb ( ...@@ -665,6 +652,7 @@ static void td_submit_urb (
/* start periodic dma if needed */ /* start periodic dma if needed */
if (periodic) { if (periodic) {
wmb ();
ohci->hc_control |= OHCI_CTRL_PLE|OHCI_CTRL_IE; ohci->hc_control |= OHCI_CTRL_PLE|OHCI_CTRL_IE;
writel (ohci->hc_control, &ohci->regs->control); writel (ohci->hc_control, &ohci->regs->control);
} }
...@@ -1053,7 +1041,7 @@ dl_done_list (struct ohci_hcd *ohci, struct td *td, struct pt_regs *regs) ...@@ -1053,7 +1041,7 @@ dl_done_list (struct ohci_hcd *ohci, struct td *td, struct pt_regs *regs)
/* clean schedule: unlink EDs that are no longer busy */ /* clean schedule: unlink EDs that are no longer busy */
if (list_empty (&ed->td_list)) if (list_empty (&ed->td_list))
ed_deschedule (ohci, ed); start_ed_unlink (ohci, ed);
/* ... reenabling halted EDs only after fault cleanup */ /* ... reenabling halted EDs only after fault cleanup */
else if ((ed->hwINFO & (ED_SKIP | ED_DEQUEUE)) == ED_SKIP) { else if ((ed->hwINFO & (ED_SKIP | ED_DEQUEUE)) == ED_SKIP) {
td = list_entry (ed->td_list.next, struct td, td_list); td = list_entry (ed->td_list.next, struct td, td_list);
......
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