aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2011-04-05 13:36:15 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-04-13 18:44:02 -0400
commit94ae4976e253757e9b03a44d27d41b20f1829d80 (patch)
tree4ef5bdb65b1c968716fc7dc523bdd209b53793b4 /drivers/usb/host
parent505d1f69ec4f8697a74711fb3a01ed151fda3834 (diff)
USB: EHCI: unlink unused QHs when the controller is stopped
This patch (as1458) fixes a problem affecting ultra-reliable systems: When hardware failover of an EHCI controller occurs, the data structures do not get released correctly. This is because the routine responsible for removing unused QHs from the async schedule assumes the controller is running properly (the frame counter is used in determining how long the QH has been idle) -- but when a failover causes the controller to be electronically disconnected from the PCI bus, obviously it stops running. The solution is simple: Allow scan_async() to remove a QH from the async schedule if it has been idle for long enough _or_ if the controller is stopped. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Reported-and-Tested-by: Dan Duval <dan.duval@stratus.com> CC: <stable@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/ehci-q.c15
1 files changed, 9 insertions, 6 deletions
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 98ded66e8d3f..42abd0f603bf 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -1247,24 +1247,27 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
1247 1247
1248static void scan_async (struct ehci_hcd *ehci) 1248static void scan_async (struct ehci_hcd *ehci)
1249{ 1249{
1250 bool stopped;
1250 struct ehci_qh *qh; 1251 struct ehci_qh *qh;
1251 enum ehci_timer_action action = TIMER_IO_WATCHDOG; 1252 enum ehci_timer_action action = TIMER_IO_WATCHDOG;
1252 1253
1253 ehci->stamp = ehci_readl(ehci, &ehci->regs->frame_index); 1254 ehci->stamp = ehci_readl(ehci, &ehci->regs->frame_index);
1254 timer_action_done (ehci, TIMER_ASYNC_SHRINK); 1255 timer_action_done (ehci, TIMER_ASYNC_SHRINK);
1255rescan: 1256rescan:
1257 stopped = !HC_IS_RUNNING(ehci_to_hcd(ehci)->state);
1256 qh = ehci->async->qh_next.qh; 1258 qh = ehci->async->qh_next.qh;
1257 if (likely (qh != NULL)) { 1259 if (likely (qh != NULL)) {
1258 do { 1260 do {
1259 /* clean any finished work for this qh */ 1261 /* clean any finished work for this qh */
1260 if (!list_empty (&qh->qtd_list) 1262 if (!list_empty(&qh->qtd_list) && (stopped ||
1261 && qh->stamp != ehci->stamp) { 1263 qh->stamp != ehci->stamp)) {
1262 int temp; 1264 int temp;
1263 1265
1264 /* unlinks could happen here; completion 1266 /* unlinks could happen here; completion
1265 * reporting drops the lock. rescan using 1267 * reporting drops the lock. rescan using
1266 * the latest schedule, but don't rescan 1268 * the latest schedule, but don't rescan
1267 * qhs we already finished (no looping). 1269 * qhs we already finished (no looping)
1270 * unless the controller is stopped.
1268 */ 1271 */
1269 qh = qh_get (qh); 1272 qh = qh_get (qh);
1270 qh->stamp = ehci->stamp; 1273 qh->stamp = ehci->stamp;
@@ -1285,9 +1288,9 @@ rescan:
1285 */ 1288 */
1286 if (list_empty(&qh->qtd_list) 1289 if (list_empty(&qh->qtd_list)
1287 && qh->qh_state == QH_STATE_LINKED) { 1290 && qh->qh_state == QH_STATE_LINKED) {
1288 if (!ehci->reclaim 1291 if (!ehci->reclaim && (stopped ||
1289 && ((ehci->stamp - qh->stamp) & 0x1fff) 1292 ((ehci->stamp - qh->stamp) & 0x1fff)
1290 >= (EHCI_SHRINK_FRAMES * 8)) 1293 >= EHCI_SHRINK_FRAMES * 8))
1291 start_unlink_async(ehci, qh); 1294 start_unlink_async(ehci, qh);
1292 else 1295 else
1293 action = TIMER_ASYNC_SHRINK; 1296 action = TIMER_ASYNC_SHRINK;