USB: fix failure path in usb_add_hcd()
authorAlan Stern <stern@rowland.harvard.edu>
Wed, 9 Jun 2010 21:34:05 +0000 (17:34 -0400)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 10 Aug 2010 21:35:33 +0000 (14:35 -0700)
This patch (as1389) fixes some errors in the failure pathway of
usb_add_hcd().  The actions it takes ought to be exactly the same as
those taken by usb_remove_hcd(), but they aren't.

In one case (removal of the usb_bus_attr_group), the two routines are
brought into agreement by changing usb_remove_hcd().  All the other
discrepancies are fixed by changing usb_add_hcd().

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/core/hcd.c

index 12742f152f431fedb567665c2ec3ce5845a98882..caae4625a1f1ff9013fe7680dfe50f232a48a1ae 100644 (file)
@@ -2229,7 +2229,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
                rhdev->speed = USB_SPEED_SUPER;
                break;
        default:
-               goto err_allocate_root_hub;
+               goto err_set_rh_speed;
        }
        hcd->self.root_hub = rhdev;
 
@@ -2305,16 +2305,29 @@ int usb_add_hcd(struct usb_hcd *hcd,
        return retval;
 
 error_create_attr_group:
+       if (HC_IS_RUNNING(hcd->state))
+               hcd->state = HC_STATE_QUIESCING;
+       spin_lock_irq(&hcd_root_hub_lock);
+       hcd->rh_registered = 0;
+       spin_unlock_irq(&hcd_root_hub_lock);
+
+#ifdef CONFIG_USB_SUSPEND
+       cancel_work_sync(&hcd->wakeup_work);
+#endif
        mutex_lock(&usb_bus_list_lock);
        usb_disconnect(&hcd->self.root_hub);
        mutex_unlock(&usb_bus_list_lock);
 err_register_root_hub:
        hcd->driver->stop(hcd);
+       hcd->state = HC_STATE_HALT;
+       hcd->poll_rh = 0;
+       del_timer_sync(&hcd->rh_timer);
 err_hcd_driver_start:
        if (hcd->irq >= 0)
                free_irq(irqnum, hcd);
 err_request_irq:
 err_hcd_driver_setup:
+err_set_rh_speed:
        hcd->self.root_hub = NULL;
        usb_put_dev(rhdev);
 err_allocate_root_hub:
@@ -2337,6 +2350,8 @@ void usb_remove_hcd(struct usb_hcd *hcd)
 {
        dev_info(hcd->self.controller, "remove, state %x\n", hcd->state);
 
+       sysfs_remove_group(&hcd->self.root_hub->dev.kobj, &usb_bus_attr_group);
+
        if (HC_IS_RUNNING (hcd->state))
                hcd->state = HC_STATE_QUIESCING;
 
@@ -2349,7 +2364,6 @@ void usb_remove_hcd(struct usb_hcd *hcd)
        cancel_work_sync(&hcd->wakeup_work);
 #endif
 
-       sysfs_remove_group(&hcd->self.root_hub->dev.kobj, &usb_bus_attr_group);
        mutex_lock(&usb_bus_list_lock);
        usb_disconnect(&hcd->self.root_hub);
        mutex_unlock(&usb_bus_list_lock);