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

USB: OHCI: don't lose track of EDs when a controller dies

This patch fixes a bug in ohci-hcd. When an URB is unlinked, the
corresponding Endpoint Descriptor is added to the ed_rm_list and taken
off the hardware schedule. Once the ED is no longer visible to the
hardware, finish_unlinks() handles the URBs that were unlinked or have
completed. If any URBs remain attached to the ED, the ED is added
back to the hardware schedule -- but only if the controller is
running.

This fails when a controller dies. A non-empty ED does not get added
back to the hardware schedule and does not remain on the ed_rm_list;
ohci-hcd loses track of it. The remaining URBs cannot be unlinked,
which causes the USB stack to hang.

The patch changes finish_unlinks() so that non-empty EDs remain on
the ed_rm_list if the controller isn't running. This requires moving
some of the existing code around, to avoid modifying the ED's hardware
fields more than once.

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

authored by

Alan Stern and committed by
Greg Kroah-Hartman
977dcfdc 256dbcd8

+29 -17
+29 -17
drivers/usb/host/ohci-q.c
··· 311 311 * - ED_OPER: when there's any request queued, the ED gets rescheduled 312 312 * immediately. HC should be working on them. 313 313 * 314 - * - ED_IDLE: when there's no TD queue. there's no reason for the HC 315 - * to care about this ED; safe to disable the endpoint. 314 + * - ED_IDLE: when there's no TD queue or the HC isn't running. 316 315 * 317 316 * When finish_unlinks() runs later, after SOF interrupt, it will often 318 317 * complete one or more URB unlinks before making that state change. ··· 953 954 int completed, modified; 954 955 __hc32 *prev; 955 956 957 + /* Is this ED already invisible to the hardware? */ 958 + if (ed->state == ED_IDLE) 959 + goto ed_idle; 960 + 956 961 /* only take off EDs that the HC isn't using, accounting for 957 962 * frame counter wraps and EDs with partially retired TDs 958 963 */ ··· 986 983 } 987 984 } 988 985 986 + /* ED's now officially unlinked, hc doesn't see */ 987 + ed->state = ED_IDLE; 988 + if (quirk_zfmicro(ohci) && ed->type == PIPE_INTERRUPT) 989 + ohci->eds_scheduled--; 990 + ed->hwHeadP &= ~cpu_to_hc32(ohci, ED_H); 991 + ed->hwNextED = 0; 992 + wmb(); 993 + ed->hwINFO &= ~cpu_to_hc32(ohci, ED_SKIP | ED_DEQUEUE); 994 + ed_idle: 995 + 989 996 /* reentrancy: if we drop the schedule lock, someone might 990 997 * have modified this list. normally it's just prepending 991 998 * entries (which we'd ignore), but paranoia won't hurt. 992 999 */ 993 - *last = ed->ed_next; 994 - ed->ed_next = NULL; 995 1000 modified = 0; 996 1001 997 1002 /* unlink urbs as requested, but rescan the list after ··· 1057 1046 if (completed && !list_empty (&ed->td_list)) 1058 1047 goto rescan_this; 1059 1048 1060 - /* ED's now officially unlinked, hc doesn't see */ 1061 - ed->state = ED_IDLE; 1062 - if (quirk_zfmicro(ohci) && ed->type == PIPE_INTERRUPT) 1063 - ohci->eds_scheduled--; 1064 - ed->hwHeadP &= ~cpu_to_hc32(ohci, ED_H); 1065 - ed->hwNextED = 0; 1066 - wmb (); 1067 - ed->hwINFO &= ~cpu_to_hc32 (ohci, ED_SKIP | ED_DEQUEUE); 1068 - 1069 - /* but if there's work queued, reschedule */ 1070 - if (!list_empty (&ed->td_list)) { 1071 - if (ohci->rh_state == OHCI_RH_RUNNING) 1072 - ed_schedule (ohci, ed); 1049 + /* 1050 + * If no TDs are queued, take ED off the ed_rm_list. 1051 + * Otherwise, if the HC is running, reschedule. 1052 + * If not, leave it on the list for further dequeues. 1053 + */ 1054 + if (list_empty(&ed->td_list)) { 1055 + *last = ed->ed_next; 1056 + ed->ed_next = NULL; 1057 + } else if (ohci->rh_state == OHCI_RH_RUNNING) { 1058 + *last = ed->ed_next; 1059 + ed->ed_next = NULL; 1060 + ed_schedule(ohci, ed); 1061 + } else { 1062 + last = &ed->ed_next; 1073 1063 } 1074 1064 1075 1065 if (modified)