ChangeSet 1.1123.18.16, 2003/08/13 14:39:23-07:00, david-b@pacbell.net

[PATCH] usb hc cleanup-after-death, oops fix

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.


 drivers/usb/core/hcd.c |   36 ++++++++++++------------------------
 1 files changed, 12 insertions(+), 24 deletions(-)


diff -Nru a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
--- a/drivers/usb/core/hcd.c	Fri Aug 15 10:45:23 2003
+++ b/drivers/usb/core/hcd.c	Fri Aug 15 10:45:23 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);
 }
