summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2010-06-09 17:34:39 -0400
committerStephen Rothwell <sfr@canb.auug.org.au>2010-07-28 11:58:03 +1000
commit1fece2c294fb5240ce687e15881996b8fce3358e (patch)
treebabe57eb9849be5b8e976e56b8477e9121ae7f3d
parent3bb7d1bf2eba8c5cf0f0d9bb0ad57db241da7a93 (diff)
USB: UHCI: acquire spinlock before calling start_rh()
This patch (as1392) fixes a bug in uhci-hcd: The start_rh() routine is supposed to be called with the private spinlock held. If an IRQ comes in at just the wrong time, the driver will think the controller has died when in fact it simply hasn't start yet. The patch also addresses some issues that may prevent an URB from being unlinked after the controller has stopped. This is an abnormal occurrence (ordinarily the controller stops only when the entire bus is suspended and hence there are no active URBs), so the pathways haven't gotten much testing. These two changes may be a little more than is strictly necessary, but clearly they won't hurt. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/usb/host/uhci-hcd.c2
-rw-r--r--drivers/usb/host/uhci-q.c4
2 files changed, 4 insertions, 2 deletions
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index 6637e52736dd..d1dce2166eff 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -691,7 +691,9 @@ static int uhci_start(struct usb_hcd *hcd)
configure_hc(uhci);
uhci->is_initialized = 1;
+ spin_lock_irq(&uhci->lock);
start_rh(uhci);
+ spin_unlock_irq(&uhci->lock);
return 0;
/*
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
index acd582c02802..d3ade4018487 100644
--- a/drivers/usb/host/uhci-q.c
+++ b/drivers/usb/host/uhci-q.c
@@ -565,7 +565,7 @@ static void uhci_unlink_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
qh->unlink_frame = uhci->frame_number;
/* Force an interrupt so we know when the QH is fully unlinked */
- if (list_empty(&uhci->skel_unlink_qh->node))
+ if (list_empty(&uhci->skel_unlink_qh->node) || uhci->is_stopped)
uhci_set_next_interrupt(uhci);
/* Move the QH from its old list to the end of the unlinking list */
@@ -1667,7 +1667,7 @@ static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh)
qh->advance_jiffies = jiffies;
goto done;
}
- ret = 0;
+ ret = uhci->is_stopped;
}
/* The queue hasn't advanced; check for timeout */