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)
/*-------------------------------------------------------------------------*/
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;
/* note: always a synchronous unlink */
del_timer_sync (&hcd->rh_timer);
hcd->rh_timer.data = 0;
if ((unsigned long) urb == hcd->rh_timer.data) {
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;
}
......@@ -1171,8 +1187,8 @@ unlink1 (struct usb_hcd *hcd, struct urb *urb)
{
int value;
if (urb == (struct urb *) hcd->rh_timer.data)
value = usb_rh_status_dequeue (hcd, urb);
if (urb->dev == hcd->self.root_hub)
value = usb_rh_urb_dequeue (hcd, urb);
else {
/* 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)
* never get completion IRQs ... maybe even the ones we need to
* 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? "
"Different ACPI or APIC settings may help."
"\n");
......
......@@ -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_bus_init (struct usb_bus *bus);
extern int usb_rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb);
#ifdef CONFIG_PCI
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