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