aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2011-07-05 12:34:05 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-07-08 17:55:08 -0400
commit004c19682884d4f40000ce1ded53f4a1d0b18206 (patch)
tree646596c61f27983145b753549cc946e488c2a613 /drivers/usb
parent0c42a4e84502533ec40544324debe2a62836ae11 (diff)
USB: EHCI: go back to using the system clock for QH unlinks
This patch (as1477) fixes a problem affecting a few types of EHCI controller. Contrary to what one might expect, these controllers automatically stop their internal frame counter when no ports are enabled. Since ehci-hcd currently relies on the frame counter for determining when it should unlink QHs from the async schedule, those controllers run into trouble: The frame counter stops and the QHs never get unlinked. Some systems have also experienced other problems traced back to commit b963801164618e25fbdc0cd452ce49c3628b46c8 (USB: ehci-hcd unlink speedups), which made the original switch from using the system clock to using the frame counter. It never became clear what the reason was for these problems, but evidently it is related to use of the frame counter. To fix all these problems, this patch more or less reverts that commit and goes back to using the system clock. But this can't be done cleanly because other changes have since been made to the scan_async() subroutine. One of these changes involved the tricky logic that tries to avoid rescanning QHs that have already been seen when the scanning loop is restarted, which happens whenever an URB is given back. Switching back to clock-based unlinks would make this logic even more complicated. Therefore the new code doesn't rescan the entire async list whenever a giveback occurs. Instead it rescans only the current QH and continues on from there. This requires the use of a separate pointer to keep track of the next QH to scan, since the current QH may be unlinked while the scanning is in progress. That new pointer must be global, so that it can be adjusted forward whenever the _next_ QH gets unlinked. (uhci-hcd uses this same trick.) Simplification of the scanning loop removes a level of indentation, which accounts for the size of the patch. The amount of code changed is relatively small, and it isn't exactly a reversion of the b963801164 commit. This fixes Bugzilla #32432. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> CC: <stable@kernel.org> Tested-by: Matej Kenda <matejken@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/host/ehci-hcd.c8
-rw-r--r--drivers/usb/host/ehci-q.c82
-rw-r--r--drivers/usb/host/ehci.h3
3 files changed, 45 insertions, 48 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 4ee62bef372..2902199fa8f 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -90,7 +90,8 @@ static const char hcd_name [] = "ehci_hcd";
90#define EHCI_IAA_MSECS 10 /* arbitrary */ 90#define EHCI_IAA_MSECS 10 /* arbitrary */
91#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ 91#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
92#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */ 92#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */
93#define EHCI_SHRINK_FRAMES 5 /* async qh unlink delay */ 93#define EHCI_SHRINK_JIFFIES (DIV_ROUND_UP(HZ, 200) + 1)
94 /* 200-ms async qh unlink delay */
94 95
95/* Initial IRQ latency: faster than hw default */ 96/* Initial IRQ latency: faster than hw default */
96static int log2_irq_thresh = 0; // 0 to 6 97static int log2_irq_thresh = 0; // 0 to 6
@@ -148,10 +149,7 @@ timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action)
148 break; 149 break;
149 /* case TIMER_ASYNC_SHRINK: */ 150 /* case TIMER_ASYNC_SHRINK: */
150 default: 151 default:
151 /* add a jiffie since we synch against the 152 t = EHCI_SHRINK_JIFFIES;
152 * 8 KHz uframe counter.
153 */
154 t = DIV_ROUND_UP(EHCI_SHRINK_FRAMES * HZ, 1000) + 1;
155 break; 153 break;
156 } 154 }
157 mod_timer(&ehci->watchdog, t + jiffies); 155 mod_timer(&ehci->watchdog, t + jiffies);
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 5d6bc624c96..9bf3c0d983c 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -1231,6 +1231,8 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
1231 1231
1232 prev->hw->hw_next = qh->hw->hw_next; 1232 prev->hw->hw_next = qh->hw->hw_next;
1233 prev->qh_next = qh->qh_next; 1233 prev->qh_next = qh->qh_next;
1234 if (ehci->qh_scan_next == qh)
1235 ehci->qh_scan_next = qh->qh_next.qh;
1234 wmb (); 1236 wmb ();
1235 1237
1236 /* If the controller isn't running, we don't have to wait for it */ 1238 /* If the controller isn't running, we don't have to wait for it */
@@ -1256,53 +1258,49 @@ static void scan_async (struct ehci_hcd *ehci)
1256 struct ehci_qh *qh; 1258 struct ehci_qh *qh;
1257 enum ehci_timer_action action = TIMER_IO_WATCHDOG; 1259 enum ehci_timer_action action = TIMER_IO_WATCHDOG;
1258 1260
1259 ehci->stamp = ehci_readl(ehci, &ehci->regs->frame_index);
1260 timer_action_done (ehci, TIMER_ASYNC_SHRINK); 1261 timer_action_done (ehci, TIMER_ASYNC_SHRINK);
1261rescan:
1262 stopped = !HC_IS_RUNNING(ehci_to_hcd(ehci)->state); 1262 stopped = !HC_IS_RUNNING(ehci_to_hcd(ehci)->state);
1263 qh = ehci->async->qh_next.qh;
1264 if (likely (qh != NULL)) {
1265 do {
1266 /* clean any finished work for this qh */
1267 if (!list_empty(&qh->qtd_list) && (stopped ||
1268 qh->stamp != ehci->stamp)) {
1269 int temp;
1270
1271 /* unlinks could happen here; completion
1272 * reporting drops the lock. rescan using
1273 * the latest schedule, but don't rescan
1274 * qhs we already finished (no looping)
1275 * unless the controller is stopped.
1276 */
1277 qh = qh_get (qh);
1278 qh->stamp = ehci->stamp;
1279 temp = qh_completions (ehci, qh);
1280 if (qh->needs_rescan)
1281 unlink_async(ehci, qh);
1282 qh_put (qh);
1283 if (temp != 0) {
1284 goto rescan;
1285 }
1286 }
1287 1263
1288 /* unlink idle entries, reducing DMA usage as well 1264 ehci->qh_scan_next = ehci->async->qh_next.qh;
1289 * as HCD schedule-scanning costs. delay for any qh 1265 while (ehci->qh_scan_next) {
1290 * we just scanned, there's a not-unusual case that it 1266 qh = ehci->qh_scan_next;
1291 * doesn't stay idle for long. 1267 ehci->qh_scan_next = qh->qh_next.qh;
1292 * (plus, avoids some kind of re-activation race.) 1268 rescan:
1269 /* clean any finished work for this qh */
1270 if (!list_empty(&qh->qtd_list)) {
1271 int temp;
1272
1273 /*
1274 * Unlinks could happen here; completion reporting
1275 * drops the lock. That's why ehci->qh_scan_next
1276 * always holds the next qh to scan; if the next qh
1277 * gets unlinked then ehci->qh_scan_next is adjusted
1278 * in start_unlink_async().
1293 */ 1279 */
1294 if (list_empty(&qh->qtd_list) 1280 qh = qh_get(qh);
1295 && qh->qh_state == QH_STATE_LINKED) { 1281 temp = qh_completions(ehci, qh);
1296 if (!ehci->reclaim && (stopped || 1282 if (qh->needs_rescan)
1297 ((ehci->stamp - qh->stamp) & 0x1fff) 1283 unlink_async(ehci, qh);
1298 >= EHCI_SHRINK_FRAMES * 8)) 1284 qh->unlink_time = jiffies + EHCI_SHRINK_JIFFIES;
1299 start_unlink_async(ehci, qh); 1285 qh_put(qh);
1300 else 1286 if (temp != 0)
1301 action = TIMER_ASYNC_SHRINK; 1287 goto rescan;
1302 } 1288 }
1303 1289
1304 qh = qh->qh_next.qh; 1290 /* unlink idle entries, reducing DMA usage as well
1305 } while (qh); 1291 * as HCD schedule-scanning costs. delay for any qh
1292 * we just scanned, there's a not-unusual case that it
1293 * doesn't stay idle for long.
1294 * (plus, avoids some kind of re-activation race.)
1295 */
1296 if (list_empty(&qh->qtd_list)
1297 && qh->qh_state == QH_STATE_LINKED) {
1298 if (!ehci->reclaim && (stopped ||
1299 time_after_eq(jiffies, qh->unlink_time)))
1300 start_unlink_async(ehci, qh);
1301 else
1302 action = TIMER_ASYNC_SHRINK;
1303 }
1306 } 1304 }
1307 if (action == TIMER_ASYNC_SHRINK) 1305 if (action == TIMER_ASYNC_SHRINK)
1308 timer_action (ehci, TIMER_ASYNC_SHRINK); 1306 timer_action (ehci, TIMER_ASYNC_SHRINK);
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index fa3129fe1ee..e4feec3457f 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -75,6 +75,7 @@ struct ehci_hcd { /* one per controller */
75 struct ehci_qh *async; 75 struct ehci_qh *async;
76 struct ehci_qh *dummy; /* For AMD quirk use */ 76 struct ehci_qh *dummy; /* For AMD quirk use */
77 struct ehci_qh *reclaim; 77 struct ehci_qh *reclaim;
78 struct ehci_qh *qh_scan_next;
78 unsigned scanning : 1; 79 unsigned scanning : 1;
79 80
80 /* periodic schedule support */ 81 /* periodic schedule support */
@@ -119,7 +120,6 @@ struct ehci_hcd { /* one per controller */
119 struct timer_list iaa_watchdog; 120 struct timer_list iaa_watchdog;
120 struct timer_list watchdog; 121 struct timer_list watchdog;
121 unsigned long actions; 122 unsigned long actions;
122 unsigned stamp;
123 unsigned periodic_stamp; 123 unsigned periodic_stamp;
124 unsigned random_frame; 124 unsigned random_frame;
125 unsigned long next_statechange; 125 unsigned long next_statechange;
@@ -345,6 +345,7 @@ struct ehci_qh {
345 struct ehci_qh *reclaim; /* next to reclaim */ 345 struct ehci_qh *reclaim; /* next to reclaim */
346 346
347 struct ehci_hcd *ehci; 347 struct ehci_hcd *ehci;
348 unsigned long unlink_time;
348 349
349 /* 350 /*
350 * Do NOT use atomic operations for QH refcounting. On some CPUs 351 * Do NOT use atomic operations for QH refcounting. On some CPUs