Recently we've seen some oopses reported in code that cleaned up HCs after they died ... like pci-pm not letting ohci-hcd suspend until after the hardware was partly disabled, or users removing a Cardbus adapter with ehci-hcd. One root cause was that the cleanup called hcd->stop() too many times.
This patch just does the cleanup that's reasonable to do before the (dead) root hub is cleaned up: it disconnects the other devices. And based on a suggestion from Pavel, a diagnostic always appears -- avoiding mystery.
Please merge for test4; thanks in advance!
- Dave
--- 1.71/drivers/usb/core/hcd.c Tue Aug 5 09:46:38 2003
+++ edited/drivers/usb/core/hcd.c Tue Aug 12 16:07:44 2003
@@ -1494,8 +1494,16 @@
static void hcd_panic (void *_hcd)
{
- struct usb_hcd *hcd = _hcd;
- hcd->driver->stop (hcd);
+ struct usb_hcd *hcd = _hcd;
+ struct usb_device *hub = hcd->self.root_hub;
+ unsigned i;
+
+ /* hc's root hub is removed later removed in hcd->stop() */
+ hub->state = USB_STATE_NOTATTACHED;
+ for (i = 0; i < hub->maxchild; i++) {
+ if (hub->children [i])
+ usb_disconnect (&hub->children [i]);
+ }
}
/**
@@ -1508,29 +1516,9 @@
*/
void usb_hc_died (struct usb_hcd *hcd)
{
- struct list_head *devlist, *urblist;
- struct hcd_dev *dev;
- struct urb *urb;
- unsigned long flags;
-
- /* flag every pending urb as done */
- spin_lock_irqsave (&hcd_data_lock, flags);
- list_for_each (devlist, &hcd->dev_list) {
- dev = list_entry (devlist, struct hcd_dev, dev_list);
- list_for_each (urblist, &dev->urb_list) {
- urb = list_entry (urblist, struct urb, urb_list);
- dev_dbg (hcd->controller, "shutdown %s urb %p pipe %x, current
status %d\n",
- hcd->self.bus_name, urb, urb->pipe, urb->status);
- if (urb->status == -EINPROGRESS)
- urb->status = -ESHUTDOWN;
- }
- }
- urb = (struct urb *) hcd->rh_timer.data;
- if (urb)
- urb->status = -ESHUTDOWN;
- spin_unlock_irqrestore (&hcd_data_lock, flags);
+ dev_err (hcd->controller, "HC died; cleaning up\n");
- /* hcd->stop() needs a task context */
+ /* clean up old urbs and devices; needs a task context */
INIT_WORK (&hcd->work, hcd_panic, hcd);
(void) schedule_work (&hcd->work);
}
