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

UHCI: Fix problem caused by lack of terminating QH

This patch (as871) fixes a problem introduced by an earlier change.
It turns out that some systems really do need to have a terminating
skeleton QH present whenever FSBR is on. I don't know any way to tell
which systems do need it and which don't; the easiest answer is to
have it there always.

This fixes the NumLock-hang bug reported by Jiri Slaby.

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

authored by

Alan Stern and committed by
Greg Kroah-Hartman
e009f1b2 e0f2e3a0

+41 -82
+15 -11
drivers/usb/host/uhci-debug.c
··· 145 145 return out - buf; 146 146 } 147 147 148 - static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space) 148 + static int uhci_show_qh(struct uhci_hcd *uhci, 149 + struct uhci_qh *qh, char *buf, int len, int space) 149 150 { 150 151 char *out = buf; 151 152 int i, nurbs; ··· 191 190 192 191 if (list_empty(&qh->queue)) { 193 192 out += sprintf(out, "%*s queue is empty\n", space, ""); 193 + if (qh == uhci->skel_async_qh) 194 + out += uhci_show_td(uhci->term_td, out, 195 + len - (out - buf), 0); 194 196 } else { 195 197 struct urb_priv *urbp = list_entry(qh->queue.next, 196 198 struct urb_priv, node); ··· 347 343 struct list_head *tmp, *head; 348 344 int nframes, nerrs; 349 345 __le32 link; 346 + __le32 fsbr_link; 350 347 351 348 static const char * const qh_names[] = { 352 349 "unlink", "iso", "int128", "int64", "int32", "int16", ··· 429 424 430 425 out += sprintf(out, "Skeleton QHs\n"); 431 426 427 + fsbr_link = 0; 432 428 for (i = 0; i < UHCI_NUM_SKELQH; ++i) { 433 429 int cnt = 0; 434 - __le32 fsbr_link = 0; 435 430 436 431 qh = uhci->skelqh[i]; 437 432 out += sprintf(out, "- skel_%s_qh\n", qh_names[i]); \ 438 - out += uhci_show_qh(qh, out, len - (out - buf), 4); 433 + out += uhci_show_qh(uhci, qh, out, len - (out - buf), 4); 439 434 440 435 /* Last QH is the Terminating QH, it's different */ 441 436 if (i == SKEL_TERM) { 442 437 if (qh_element(qh) != LINK_TO_TD(uhci->term_td)) 443 438 out += sprintf(out, " skel_term_qh element is not set to term_td!\n"); 444 - if (link == LINK_TO_QH(uhci->skel_term_qh)) 445 - goto check_qh_link; 446 - continue; 439 + link = fsbr_link; 440 + if (!link) 441 + link = LINK_TO_QH(uhci->skel_term_qh); 442 + goto check_qh_link; 447 443 } 448 444 449 445 head = &qh->node; ··· 454 448 qh = list_entry(tmp, struct uhci_qh, node); 455 449 tmp = tmp->next; 456 450 if (++cnt <= 10) 457 - out += uhci_show_qh(qh, out, 451 + out += uhci_show_qh(uhci, qh, out, 458 452 len - (out - buf), 4); 459 453 if (!fsbr_link && qh->skel >= SKEL_FSBR) 460 454 fsbr_link = LINK_TO_QH(qh); ··· 469 463 link = LINK_TO_QH(uhci->skel_async_qh); 470 464 else if (!uhci->fsbr_is_on) 471 465 ; 472 - else if (fsbr_link) 473 - link = fsbr_link; 474 466 else 475 467 link = LINK_TO_QH(uhci->skel_term_qh); 476 468 check_qh_link: ··· 577 573 static inline void lprintk(char *buf) 578 574 {} 579 575 580 - static inline int uhci_show_qh(struct uhci_qh *qh, char *buf, 581 - int len, int space) 576 + static inline int uhci_show_qh(struct uhci_hcd *uhci, 577 + struct uhci_qh *qh, char *buf, int len, int space) 582 578 { 583 579 return 0; 584 580 }
+2 -1
drivers/usb/host/uhci-hcd.c
··· 632 632 */ 633 633 for (i = SKEL_ISO + 1; i < SKEL_ASYNC; ++i) 634 634 uhci->skelqh[i]->link = LINK_TO_QH(uhci->skel_async_qh); 635 - uhci->skel_async_qh->link = uhci->skel_term_qh->link = UHCI_PTR_TERM; 635 + uhci->skel_async_qh->link = UHCI_PTR_TERM; 636 + uhci->skel_term_qh->link = LINK_TO_QH(uhci->skel_term_qh); 636 637 637 638 /* This dummy TD is to work around a bug in Intel PIIX controllers */ 638 639 uhci_fill_td(uhci->term_td, 0, uhci_explen(0) |
+24 -70
drivers/usb/host/uhci-q.c
··· 45 45 */ 46 46 static void uhci_fsbr_on(struct uhci_hcd *uhci) 47 47 { 48 - struct uhci_qh *fsbr_qh, *lqh, *tqh; 48 + struct uhci_qh *lqh; 49 49 50 + /* The terminating skeleton QH always points back to the first 51 + * FSBR QH. Make the last async QH point to the terminating 52 + * skeleton QH. */ 50 53 uhci->fsbr_is_on = 1; 51 54 lqh = list_entry(uhci->skel_async_qh->node.prev, 52 55 struct uhci_qh, node); 53 - 54 - /* Find the first FSBR QH. Linear search through the list is 55 - * acceptable because normally FSBR gets turned on as soon as 56 - * one QH needs it. */ 57 - fsbr_qh = NULL; 58 - list_for_each_entry_reverse(tqh, &uhci->skel_async_qh->node, node) { 59 - if (tqh->skel < SKEL_FSBR) 60 - break; 61 - fsbr_qh = tqh; 62 - } 63 - 64 - /* No FSBR QH means we must insert the terminating skeleton QH */ 65 - if (!fsbr_qh) { 66 - uhci->skel_term_qh->link = LINK_TO_QH(uhci->skel_term_qh); 67 - wmb(); 68 - lqh->link = uhci->skel_term_qh->link; 69 - 70 - /* Otherwise loop the last QH to the first FSBR QH */ 71 - } else 72 - lqh->link = LINK_TO_QH(fsbr_qh); 56 + lqh->link = LINK_TO_QH(uhci->skel_term_qh); 73 57 } 74 58 75 59 static void uhci_fsbr_off(struct uhci_hcd *uhci) 76 60 { 77 61 struct uhci_qh *lqh; 78 62 63 + /* Remove the link from the last async QH to the terminating 64 + * skeleton QH. */ 79 65 uhci->fsbr_is_on = 0; 80 66 lqh = list_entry(uhci->skel_async_qh->node.prev, 81 67 struct uhci_qh, node); 82 - 83 - /* End the async list normally and unlink the terminating QH */ 84 - lqh->link = uhci->skel_term_qh->link = UHCI_PTR_TERM; 68 + lqh->link = UHCI_PTR_TERM; 85 69 } 86 70 87 71 static void uhci_add_fsbr(struct uhci_hcd *uhci, struct urb *urb) ··· 448 464 */ 449 465 static void link_async(struct uhci_hcd *uhci, struct uhci_qh *qh) 450 466 { 451 - struct uhci_qh *pqh, *lqh; 467 + struct uhci_qh *pqh; 452 468 __le32 link_to_new_qh; 453 - __le32 *extra_link = &link_to_new_qh; 454 469 455 470 /* Find the predecessor QH for our new one and insert it in the list. 456 471 * The list of QHs is expected to be short, so linear search won't ··· 459 476 break; 460 477 } 461 478 list_add(&qh->node, &pqh->node); 462 - qh->link = pqh->link; 463 - 464 - link_to_new_qh = LINK_TO_QH(qh); 465 - 466 - /* If this is now the first FSBR QH, take special action */ 467 - if (uhci->fsbr_is_on && pqh->skel < SKEL_FSBR && 468 - qh->skel >= SKEL_FSBR) { 469 - lqh = list_entry(uhci->skel_async_qh->node.prev, 470 - struct uhci_qh, node); 471 - 472 - /* If the new QH is also the last one, we must unlink 473 - * the terminating skeleton QH and make the new QH point 474 - * back to itself. */ 475 - if (qh == lqh) { 476 - qh->link = link_to_new_qh; 477 - extra_link = &uhci->skel_term_qh->link; 478 - 479 - /* Otherwise the last QH must point to the new QH */ 480 - } else 481 - extra_link = &lqh->link; 482 - } 483 479 484 480 /* Link it into the schedule */ 481 + qh->link = pqh->link; 485 482 wmb(); 486 - *extra_link = pqh->link = link_to_new_qh; 483 + link_to_new_qh = LINK_TO_QH(qh); 484 + pqh->link = link_to_new_qh; 485 + 486 + /* If this is now the first FSBR QH, link the terminating skeleton 487 + * QH to it. */ 488 + if (pqh->skel < SKEL_FSBR && qh->skel >= SKEL_FSBR) 489 + uhci->skel_term_qh->link = link_to_new_qh; 487 490 } 488 491 489 492 /* ··· 530 561 */ 531 562 static void unlink_async(struct uhci_hcd *uhci, struct uhci_qh *qh) 532 563 { 533 - struct uhci_qh *pqh, *lqh; 564 + struct uhci_qh *pqh; 534 565 __le32 link_to_next_qh = qh->link; 535 566 536 567 pqh = list_entry(qh->node.prev, struct uhci_qh, node); 537 - 538 - /* If this is the first FSBQ QH, take special action */ 539 - if (uhci->fsbr_is_on && pqh->skel < SKEL_FSBR && 540 - qh->skel >= SKEL_FSBR) { 541 - lqh = list_entry(uhci->skel_async_qh->node.prev, 542 - struct uhci_qh, node); 543 - 544 - /* If this QH is also the last one, we must link in 545 - * the terminating skeleton QH. */ 546 - if (qh == lqh) { 547 - link_to_next_qh = LINK_TO_QH(uhci->skel_term_qh); 548 - uhci->skel_term_qh->link = link_to_next_qh; 549 - wmb(); 550 - qh->link = link_to_next_qh; 551 - 552 - /* Otherwise the last QH must point to the new first FSBR QH */ 553 - } else 554 - lqh->link = link_to_next_qh; 555 - } 556 - 557 568 pqh->link = link_to_next_qh; 569 + 570 + /* If this was the old first FSBR QH, link the terminating skeleton 571 + * QH to the next (new first FSBR) QH. */ 572 + if (pqh->skel < SKEL_FSBR && qh->skel >= SKEL_FSBR) 573 + uhci->skel_term_qh->link = link_to_next_qh; 558 574 mb(); 559 575 } 560 576 ··· 1171 1217 1172 1218 if (debug > 1 && errbuf) { 1173 1219 /* Print the chain for debugging */ 1174 - uhci_show_qh(urbp->qh, errbuf, 1220 + uhci_show_qh(uhci, urbp->qh, errbuf, 1175 1221 ERRBUF_LEN, 0); 1176 1222 lprintk(errbuf); 1177 1223 }