Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

USB: EHCI: improve handling of the ehci->iaa_in_progress flag

This patch improves the way ehci-hcd handles the iaa_in_progress flag.
The current code is somewhat careless in this regard:

The flag is meaningless when the root hub isn't running, most
particularly after the root hub has been suspended. But in
start_iaa_cycle(), the driver checks the flag before checking
the root hub's state. They should be checked in the opposite
order.

That routine also sets the flag too early, before it has
definitely committed to starting an IAA cycle.

The flag is turned off in end_unlink_async(). Upcoming
changes will call that routine at other times, not just at the
end of an IAA cycle. The two actions are logically separate
(although related), so we separate out a new routine to be
called in place of end_unlink_async() whenever an IAA cycle
ends: end_iaa_cycle().

iaa_in_progress should be turned off when the root hub is
suspended -- we certainly don't want it still to be set when
the root hub resumes. Therefore the call to
end_unlink_async() in ehci_bus_suspend() should also be
replaced with a call to end_iaa_cycle().

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Alan Stern and committed by
Greg Kroah-Hartman
f96fba0d fcc5184e

+20 -16
+2 -1
drivers/usb/host/ehci-hcd.c
··· 306 306 307 307 /*-------------------------------------------------------------------------*/ 308 308 309 + static void end_iaa_cycle(struct ehci_hcd *ehci); 309 310 static void end_unlink_async(struct ehci_hcd *ehci); 310 311 static void unlink_empty_async(struct ehci_hcd *ehci); 311 312 static void unlink_empty_async_suspended(struct ehci_hcd *ehci); ··· 759 758 ehci_dbg(ehci, "IAA with IAAD still set?\n"); 760 759 if (ehci->iaa_in_progress) 761 760 COUNT(ehci->stats.iaa); 762 - end_unlink_async(ehci); 761 + end_iaa_cycle(ehci); 763 762 } 764 763 765 764 /* remote wakeup [4.3.1] */
+2 -1
drivers/usb/host/ehci-hub.c
··· 347 347 goto done; 348 348 ehci->rh_state = EHCI_RH_SUSPENDED; 349 349 350 - end_unlink_async(ehci); 350 + /* Any IAA cycle that started before the suspend is now invalid */ 351 + end_iaa_cycle(ehci); 351 352 unlink_empty_async_suspended(ehci); 352 353 ehci_handle_start_intr_unlinks(ehci); 353 354 ehci_handle_intr_unlinks(ehci);
+15 -13
drivers/usb/host/ehci-q.c
··· 1283 1283 1284 1284 static void start_iaa_cycle(struct ehci_hcd *ehci) 1285 1285 { 1286 - /* Do nothing if an IAA cycle is already running */ 1287 - if (ehci->iaa_in_progress) 1288 - return; 1289 - ehci->iaa_in_progress = true; 1290 - 1291 1286 /* If the controller isn't running, we don't have to wait for it */ 1292 1287 if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) { 1293 1288 end_unlink_async(ehci); 1294 1289 1295 - /* Otherwise start a new IAA cycle */ 1296 - } else if (likely(ehci->rh_state == EHCI_RH_RUNNING)) { 1290 + /* Otherwise start a new IAA cycle if one isn't already running */ 1291 + } else if (ehci->rh_state == EHCI_RH_RUNNING && 1292 + !ehci->iaa_in_progress) { 1297 1293 1298 1294 /* Make sure the unlinks are all visible to the hardware */ 1299 1295 wmb(); ··· 1297 1301 ehci_writel(ehci, ehci->command | CMD_IAAD, 1298 1302 &ehci->regs->command); 1299 1303 ehci_readl(ehci, &ehci->regs->command); 1304 + ehci->iaa_in_progress = true; 1300 1305 ehci_enable_event(ehci, EHCI_HRTIMER_IAA_WATCHDOG, true); 1301 1306 } 1302 1307 } 1303 1308 1304 - /* the async qh for the qtds being unlinked are now gone from the HC */ 1305 - 1306 - static void end_unlink_async(struct ehci_hcd *ehci) 1309 + static void end_iaa_cycle(struct ehci_hcd *ehci) 1307 1310 { 1308 - struct ehci_qh *qh; 1309 - bool early_exit; 1310 - 1311 1311 if (ehci->has_synopsys_hc_bug) 1312 1312 ehci_writel(ehci, (u32) ehci->async->qh_dma, 1313 1313 &ehci->regs->async_next); 1314 1314 1315 1315 /* The current IAA cycle has ended */ 1316 1316 ehci->iaa_in_progress = false; 1317 + 1318 + end_unlink_async(ehci); 1319 + } 1320 + 1321 + /* See if the async qh for the qtds being unlinked are now gone from the HC */ 1322 + 1323 + static void end_unlink_async(struct ehci_hcd *ehci) 1324 + { 1325 + struct ehci_qh *qh; 1326 + bool early_exit; 1317 1327 1318 1328 if (list_empty(&ehci->async_unlink)) 1319 1329 return;
+1 -1
drivers/usb/host/ehci-timer.c
··· 361 361 } 362 362 363 363 ehci_dbg(ehci, "IAA watchdog: status %x cmd %x\n", status, cmd); 364 - end_unlink_async(ehci); 364 + end_iaa_cycle(ehci); 365 365 } 366 366 367 367