Commit 9a53bdfb authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[PATCH] ohci-hcd, fix urb unlink races

This patch:

- Fixes a longstanding urb unlink race, by switching to a single queue
  for EDs being unlinked.  The previous two-queue scheme was sensitive to
  IRQ latencies:  one extra millisecond would make it use the wrong queue.
  This updated scheme should handle latencies of up to 32K microseconds
  (Cthulu forfend :) and slightly shrinks object code size.

- Related (mostly) cleanup.  Some functions and one ED field renamed, ED
  layout is a smidgeon more compact (even given more data), driver version
  string doesn't reflect CVS, debug message only comes out in verbose mode.
parent 1fe62d53
......@@ -12,6 +12,9 @@
*
* History:
*
* 2002/06/01 remember frame when HC won't see EDs any more; use that info
* to fix urb unlink races caused by interrupt latency assumptions;
* minor ED field and function naming updates
* 2002/01/18 package as a patch for 2.5.3; this should match the
* 2.4.17 kernel modulo some bugs being fixed.
*
......@@ -106,7 +109,7 @@
* - lots more testing!!
*/
#define DRIVER_VERSION "$Revision: 1.9 $"
#define DRIVER_VERSION "2002-Jun-01"
#define DRIVER_AUTHOR "Roman Weissgaerber <weissg@vienna.at>, David Brownell"
#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
......@@ -287,7 +290,7 @@ static int ohci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
}
urb_priv->state = URB_DEL;
ed_unlink (urb->dev, urb_priv->ed);
start_urb_unlink (ohci, urb_priv->ed);
spin_unlock_irqrestore (&ohci->lock, flags);
} else {
/*
......@@ -508,16 +511,15 @@ static void ohci_irq (struct usb_hcd *hcd)
/* could track INTR_SO to reduce available PCI/... bandwidth */
// FIXME: this assumes SOF (1/ms) interrupts don't get lost...
if (ints & OHCI_INTR_SF) {
unsigned int frame = le16_to_cpu (ohci->hcca->frame_no) & 1;
/* handle any pending URB/ED unlinks, leaving INTR_SF enabled
* when there's still unlinking to be done (next frame).
*/
spin_lock (&ohci->lock);
if (ohci->ed_rm_list)
finish_unlinks (ohci, le16_to_cpu (ohci->hcca->frame_no));
if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list)
writel (OHCI_INTR_SF, &regs->intrdisable);
if (ohci->ed_rm_list [!frame] != NULL) {
dl_del_list (ohci, !frame);
}
if (ohci->ed_rm_list [frame] != NULL)
writel (OHCI_INTR_SF, &regs->intrenable);
}
spin_unlock (&ohci->lock);
writel (ints, &regs->intrstatus);
writel (OHCI_INTR_MIE, &regs->intrenable);
......@@ -719,8 +721,7 @@ static int hc_restart (struct ohci_hcd *ohci)
for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table [i] = 0;
/* no EDs to remove */
ohci->ed_rm_list [0] = NULL;
ohci->ed_rm_list [1] = NULL;
ohci->ed_rm_list = NULL;
/* empty control and bulk lists */
ohci->ed_isotail = NULL;
......@@ -802,7 +803,7 @@ static int ohci_resume (struct usb_hcd *hcd)
ohci->disabled = 0;
ohci->sleeping = 0;
ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
if (!ohci->ed_rm_list [0] && !ohci->ed_rm_list [1]) {
if (!ohci->ed_rm_list) {
if (ohci->ed_controltail)
ohci->hc_control |= OHCI_CTRL_CLE;
if (ohci->ed_bulktail)
......
This diff is collapsed.
......@@ -27,22 +27,29 @@ struct ed {
__u32 hwNextED; /* next ED in list */
/* rest are purely for the driver's use */
struct ed *ed_prev;
__u8 int_period;
__u8 int_branch;
__u8 int_load;
__u8 int_interval;
__u8 state; // ED_{NEW,UNLINK,OPER}
dma_addr_t dma; /* addr of ED */
struct ed *ed_prev; /* for non-interrupt EDs */
u8 type; /* PIPE_{BULK,...} */
u8 interval; /* interrupt, isochronous */
union {
struct intr_info { /* interrupt */
u8 int_period;
u8 int_branch;
u8 int_load;
};
u16 last_iso; /* isochronous */
};
u8 state; /* ED_{NEW,UNLINK,OPER} */
#define ED_NEW 0x00 /* unused, no dummy td */
#define ED_UNLINK 0x01 /* dummy td, maybe linked to hc */
#define ED_OPER 0x02 /* dummy td, _is_ linked to hc */
#define ED_URB_DEL 0x08 /* for unlinking; masked in */
__u8 type;
__u16 last_iso;
/* HC may see EDs on rm_list until next frame (frame_no == tick) */
u16 tick;
struct ed *ed_rm_list;
dma_addr_t dma; /* addr of ED */
} __attribute__ ((aligned(16)));
#define ED_MASK ((u32)~0x0f) /* strip hw status in low addr bits */
......@@ -335,7 +342,7 @@ struct ohci_hcd {
struct ohci_hcca *hcca;
dma_addr_t hcca_dma;
struct ed *ed_rm_list [2]; /* to be removed */
struct ed *ed_rm_list; /* to be removed */
struct ed *ed_bulktail; /* last in bulk list */
struct ed *ed_controltail; /* last in ctrl 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