aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2013-01-25 16:54:22 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-01-25 16:58:20 -0500
commit6e0c3339a6f19d748f16091d0a05adeb1e1f822b (patch)
tree0afaafdaaaca7df3362dde760b7d0273f508f04f /drivers/usb/host
parent55bcdce8a8228223ec4d17d8ded8134ed265d2c5 (diff)
USB: EHCI: unlink one async QH at a time
This patch (as1648) fixes a regression affecting nVidia EHCI controllers. Evidently they don't like to have more than one async QH unlinked at a time. I can't imagine how they manage to mess it up, but at least one of them does. The patch changes the async unlink logic in two ways: Each time an IAA cycle is started, only the first QH on the async unlink list is handled (rather than all of them). Async QHs do not all get unlinked as soon as they have been empty for long enough. Instead, only the last one (i.e., the one that has been on the schedule the longest) is unlinked, and then only if no other unlinks are in progress at the time. This means that when multiple QHs are empty, they won't be unlinked as quickly as before. That's okay; it won't affect correct operation of the driver or add an excessive load. Multiple unlinks tend to be relatively rare in any case. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Reported-and-tested-by: Piergiorgio Sartor <piergiorgio.sartor@nexgo.de> Cc: stable <stable@vger.kernel.org> # 3.6 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/ehci-q.c50
1 files changed, 30 insertions, 20 deletions
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 3d989028c836..fd252f0cfb3a 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -1197,17 +1197,26 @@ static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested)
1197 if (ehci->async_iaa || ehci->async_unlinking) 1197 if (ehci->async_iaa || ehci->async_unlinking)
1198 return; 1198 return;
1199 1199
1200 /* Do all the waiting QHs at once */
1201 ehci->async_iaa = ehci->async_unlink;
1202 ehci->async_unlink = NULL;
1203
1204 /* If the controller isn't running, we don't have to wait for it */ 1200 /* If the controller isn't running, we don't have to wait for it */
1205 if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) { 1201 if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) {
1202
1203 /* Do all the waiting QHs */
1204 ehci->async_iaa = ehci->async_unlink;
1205 ehci->async_unlink = NULL;
1206
1206 if (!nested) /* Avoid recursion */ 1207 if (!nested) /* Avoid recursion */
1207 end_unlink_async(ehci); 1208 end_unlink_async(ehci);
1208 1209
1209 /* Otherwise start a new IAA cycle */ 1210 /* Otherwise start a new IAA cycle */
1210 } else if (likely(ehci->rh_state == EHCI_RH_RUNNING)) { 1211 } else if (likely(ehci->rh_state == EHCI_RH_RUNNING)) {
1212 struct ehci_qh *qh;
1213
1214 /* Do only the first waiting QH (nVidia bug?) */
1215 qh = ehci->async_unlink;
1216 ehci->async_iaa = qh;
1217 ehci->async_unlink = qh->unlink_next;
1218 qh->unlink_next = NULL;
1219
1211 /* Make sure the unlinks are all visible to the hardware */ 1220 /* Make sure the unlinks are all visible to the hardware */
1212 wmb(); 1221 wmb();
1213 1222
@@ -1255,34 +1264,35 @@ static void end_unlink_async(struct ehci_hcd *ehci)
1255 } 1264 }
1256} 1265}
1257 1266
1267static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh);
1268
1258static void unlink_empty_async(struct ehci_hcd *ehci) 1269static void unlink_empty_async(struct ehci_hcd *ehci)
1259{ 1270{
1260 struct ehci_qh *qh, *next; 1271 struct ehci_qh *qh;
1261 bool stopped = (ehci->rh_state < EHCI_RH_RUNNING); 1272 struct ehci_qh *qh_to_unlink = NULL;
1262 bool check_unlinks_later = false; 1273 bool check_unlinks_later = false;
1274 int count = 0;
1263 1275
1264 /* Unlink all the async QHs that have been empty for a timer cycle */ 1276 /* Find the last async QH which has been empty for a timer cycle */
1265 next = ehci->async->qh_next.qh; 1277 for (qh = ehci->async->qh_next.qh; qh; qh = qh->qh_next.qh) {
1266 while (next) {
1267 qh = next;
1268 next = qh->qh_next.qh;
1269
1270 if (list_empty(&qh->qtd_list) && 1278 if (list_empty(&qh->qtd_list) &&
1271 qh->qh_state == QH_STATE_LINKED) { 1279 qh->qh_state == QH_STATE_LINKED) {
1272 if (!stopped && qh->unlink_cycle == 1280 ++count;
1273 ehci->async_unlink_cycle) 1281 if (qh->unlink_cycle == ehci->async_unlink_cycle)
1274 check_unlinks_later = true; 1282 check_unlinks_later = true;
1275 else 1283 else
1276 single_unlink_async(ehci, qh); 1284 qh_to_unlink = qh;
1277 } 1285 }
1278 } 1286 }
1279 1287
1280 /* Start a new IAA cycle if any QHs are waiting for it */ 1288 /* If nothing else is being unlinked, unlink the last empty QH */
1281 if (ehci->async_unlink) 1289 if (!ehci->async_iaa && !ehci->async_unlink && qh_to_unlink) {
1282 start_iaa_cycle(ehci, false); 1290 start_unlink_async(ehci, qh_to_unlink);
1291 --count;
1292 }
1283 1293
1284 /* QHs that haven't been empty for long enough will be handled later */ 1294 /* Other QHs will be handled later */
1285 if (check_unlinks_later) { 1295 if (count > 0) {
1286 ehci_enable_event(ehci, EHCI_HRTIMER_ASYNC_UNLINKS, true); 1296 ehci_enable_event(ehci, EHCI_HRTIMER_ASYNC_UNLINKS, true);
1287 ++ehci->async_unlink_cycle; 1297 ++ehci->async_unlink_cycle;
1288 } 1298 }