diff options
author | Pete Zaitcev <zaitcev@redhat.com> | 2008-06-02 00:23:07 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-07-21 18:16:25 -0400 |
commit | 6deb270b5c60680ca9117bd545302ea6a58bad42 (patch) | |
tree | a1edb9b518004c5939a29ac98a8314835b7e5166 /drivers/usb/host | |
parent | 6381fad77e5d44f8e0e2afffe686cb4e6fc36e71 (diff) |
USB: ohci_hcd hang: submit vs. rmmod race
If we do rmmod ohci_hcd while an application is doing something, the
following may happen:
- a control URB completes (in finish_urb) and the ohci's endpoint is
set into ED_UNLINK in ed_deschedule
- same URB is (re)submitted because of the open/close loop or other
such application behaviour
- rmmod sets the state to HC_STATE_QUESCING
- finish_unlinks happens at next SOF; normally it would set ed into
ED_IDLE and immediately call ed_schedule (since URB had extra TDs
queued), which sets it into ED_OPER. But the check in ed_schedule
makes it fail with -EAGAIN (which is ignored)
- from now on we have a dead URB stuck; it cannot even be unlinked
because the ed status is not ED_OPER, and thus start_ed_unlink is
not invoked.
This patch removes the check. In 2.6.25, all callers check for
__ACTIVE bit before invoking ed_schedule, which is more appropriate.
Alan Stern and David Brownell approved of this (cautiously).
Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host')
-rw-r--r-- | drivers/usb/host/ohci-q.c | 3 |
1 files changed, 0 insertions, 3 deletions
diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index 9b547407c934..6a9b4c557953 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c | |||
@@ -159,9 +159,6 @@ static int ed_schedule (struct ohci_hcd *ohci, struct ed *ed) | |||
159 | { | 159 | { |
160 | int branch; | 160 | int branch; |
161 | 161 | ||
162 | if (ohci_to_hcd(ohci)->state == HC_STATE_QUIESCING) | ||
163 | return -EAGAIN; | ||
164 | |||
165 | ed->state = ED_OPER; | 162 | ed->state = ED_OPER; |
166 | ed->ed_prev = NULL; | 163 | ed->ed_prev = NULL; |
167 | ed->ed_next = NULL; | 164 | ed->ed_next = NULL; |