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

[PATCH] USB: Dequeuing of root-hub URBs

This patch fixes a tiny SMP-type hole in root-hub synchronization.
Although the HCD glue layer properly unlinks root-hub status URBs
synchronously, it doesn't do so for URBs sent to endpoint 0.  This patch
copies some code from usb_kill_urb, to make such unlinks wait until the
host controller driver has finished handling the URB.  This behavior is
required for hcd_endpoint_disable to work correctly.

The patch also renames usb_rh_status_dequeue to usb_rh_urb_dequeue (to
better describe its updated function) and declares the routine static.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent 2bff2da2
...@@ -568,18 +568,34 @@ static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb) ...@@ -568,18 +568,34 @@ static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
int usb_rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb) static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
{ {
unsigned long flags; unsigned long flags;
/* note: always a synchronous unlink */ /* note: always a synchronous unlink */
del_timer_sync (&hcd->rh_timer); if ((unsigned long) urb == hcd->rh_timer.data) {
hcd->rh_timer.data = 0; del_timer_sync (&hcd->rh_timer);
hcd->rh_timer.data = 0;
local_irq_save (flags);
urb->hcpriv = NULL;
usb_hcd_giveback_urb (hcd, urb, NULL);
local_irq_restore (flags);
} else if (usb_pipeendpoint(urb->pipe) == 0) {
spin_lock_irq(&urb->lock); /* from usb_kill_urb */
++urb->reject;
spin_unlock_irq(&urb->lock);
wait_event(usb_kill_urb_queue,
atomic_read(&urb->use_count) == 0);
spin_lock_irq(&urb->lock);
--urb->reject;
spin_unlock_irq(&urb->lock);
} else
return -EINVAL;
local_irq_save (flags);
urb->hcpriv = NULL;
usb_hcd_giveback_urb (hcd, urb, NULL);
local_irq_restore (flags);
return 0; return 0;
} }
...@@ -1171,8 +1187,8 @@ unlink1 (struct usb_hcd *hcd, struct urb *urb) ...@@ -1171,8 +1187,8 @@ unlink1 (struct usb_hcd *hcd, struct urb *urb)
{ {
int value; int value;
if (urb == (struct urb *) hcd->rh_timer.data) if (urb->dev == hcd->self.root_hub)
value = usb_rh_status_dequeue (hcd, urb); value = usb_rh_urb_dequeue (hcd, urb);
else { else {
/* The only reason an HCD might fail this call is if /* The only reason an HCD might fail this call is if
...@@ -1260,7 +1276,7 @@ static int hcd_unlink_urb (struct urb *urb, int status) ...@@ -1260,7 +1276,7 @@ static int hcd_unlink_urb (struct urb *urb, int status)
* never get completion IRQs ... maybe even the ones we need to * never get completion IRQs ... maybe even the ones we need to
* finish unlinking the initial failed usb_set_address(). * finish unlinking the initial failed usb_set_address().
*/ */
if (!hcd->saw_irq && hcd->rh_timer.data != (unsigned long) urb) { if (!hcd->saw_irq && hcd->self.root_hub != urb->dev) {
dev_warn (hcd->self.controller, "Unlink after no-IRQ? " dev_warn (hcd->self.controller, "Unlink after no-IRQ? "
"Different ACPI or APIC settings may help." "Different ACPI or APIC settings may help."
"\n"); "\n");
......
...@@ -215,7 +215,6 @@ struct hc_driver { ...@@ -215,7 +215,6 @@ struct hc_driver {
extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs); extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs);
extern void usb_bus_init (struct usb_bus *bus); extern void usb_bus_init (struct usb_bus *bus);
extern int usb_rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb);
#ifdef CONFIG_PCI #ifdef CONFIG_PCI
struct pci_dev; struct pci_dev;
......
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