Commit 72ab0f27 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

[PATCH] USB: Minor tidying up of hub driver

After my last few changesets there were a few small items that needed to
be tidied up.

	Update kerneldoc to reflect the actual operation of
	usb_disconnect() and usb_new_device().  The new locking
	requirements are listed too, though they aren't all
	implemented yet.

	Fulfill the new locking requirement in hcd_panic().

	Remove unneeded local variables to conserve stack space in
	usb_disconnect(), which calls itself recursively.

	In hub_port_connect_change(), store the parent's children[]
	pointer as late as possible and don't lock the new device until
	then (that's when it becomes globally accessible).  This will
	minimize the time that the not-fully-configured device structure
	is visible to other parts of the kernel.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent f1eb6dba
...@@ -1579,11 +1579,13 @@ static void hcd_panic (void *_hcd) ...@@ -1579,11 +1579,13 @@ static void hcd_panic (void *_hcd)
unsigned i; unsigned i;
/* hc's root hub is removed later removed in hcd->stop() */ /* hc's root hub is removed later removed in hcd->stop() */
down (&hub->serialize);
hub->state = USB_STATE_NOTATTACHED; hub->state = USB_STATE_NOTATTACHED;
for (i = 0; i < hub->maxchild; i++) { for (i = 0; i < hub->maxchild; i++) {
if (hub->children [i]) if (hub->children [i])
usb_disconnect (&hub->children [i]); usb_disconnect (&hub->children [i]);
} }
up (&hub->serialize);
} }
/** /**
......
...@@ -868,6 +868,9 @@ static void release_address(struct usb_device *udev) ...@@ -868,6 +868,9 @@ static void release_address(struct usb_device *udev)
* Context: !in_interrupt () * Context: !in_interrupt ()
* *
* Something got disconnected. Get rid of it, and all of its children. * Something got disconnected. Get rid of it, and all of its children.
* If *pdev is a normal device then the parent hub should be locked.
* If *pdev is a root hub then this routine will acquire the
* usb_bus_list_lock on behalf of the caller.
* *
* Only hub drivers (including virtual root hub drivers for host * Only hub drivers (including virtual root hub drivers for host
* controllers) should ever call this. * controllers) should ever call this.
...@@ -877,20 +880,12 @@ static void release_address(struct usb_device *udev) ...@@ -877,20 +880,12 @@ static void release_address(struct usb_device *udev)
void usb_disconnect(struct usb_device **pdev) void usb_disconnect(struct usb_device **pdev)
{ {
struct usb_device *udev = *pdev; struct usb_device *udev = *pdev;
struct usb_bus *bus;
struct usb_operations *ops;
int i; int i;
if (!udev) { if (!udev) {
pr_debug ("%s nodev\n", __FUNCTION__); pr_debug ("%s nodev\n", __FUNCTION__);
return; return;
} }
bus = udev->bus;
if (!bus) {
pr_debug ("%s nobus\n", __FUNCTION__);
return;
}
ops = bus->op;
/* mark the device as inactive, so any further urb submissions for /* mark the device as inactive, so any further urb submissions for
* this device will fail. * this device will fail.
...@@ -906,9 +901,8 @@ void usb_disconnect(struct usb_device **pdev) ...@@ -906,9 +901,8 @@ void usb_disconnect(struct usb_device **pdev)
/* Free up all the children before we remove this device */ /* Free up all the children before we remove this device */
for (i = 0; i < USB_MAXCHILDREN; i++) { for (i = 0; i < USB_MAXCHILDREN; i++) {
struct usb_device **child = udev->children + i; if (udev->children[i])
if (*child) usb_disconnect(&udev->children[i]);
usb_disconnect(child);
} }
/* deallocate hcd/hardware state ... nuking all pending urbs and /* deallocate hcd/hardware state ... nuking all pending urbs and
...@@ -987,21 +981,23 @@ static inline void show_string(struct usb_device *udev, char *id, int index) ...@@ -987,21 +981,23 @@ static inline void show_string(struct usb_device *udev, char *id, int index)
/* /*
* usb_new_device - perform initial device setup (usbcore-internal) * usb_new_device - perform initial device setup (usbcore-internal)
* @dev: newly addressed device (in ADDRESS state) * @udev: newly addressed device (in ADDRESS state)
* *
* This is called with devices which have been enumerated, but not yet * This is called with devices which have been enumerated, but not yet
* configured. The device descriptor is available, but not descriptors * configured. The device descriptor is available, but not descriptors
* for any device configuration. The caller owns dev->serialize, and * for any device configuration. The caller must have locked udev and
* the device is not visible through sysfs or other filesystem code. * either the parent hub (if udev is a normal device) or else the
* usb_bus_list_lock (if udev is a root hub). The parent's pointer to
* udev has already been installed, but udev is not yet visible through
* sysfs or other filesystem code.
* *
* Returns 0 for success (device is configured and listed, with its * Returns 0 for success (device is configured and listed, with its
* interfaces, in sysfs); else a negative errno value. On error, one * interfaces, in sysfs); else a negative errno value.
* reference count to the device has been dropped.
* *
* This call is synchronous, and may not be used in an interrupt context. * This call is synchronous, and may not be used in an interrupt context.
* *
* Only the hub driver should ever call this; root hub registration * Only the hub driver should ever call this; root hub registration
* uses it only indirectly. * uses it indirectly.
*/ */
int usb_new_device(struct usb_device *udev) int usb_new_device(struct usb_device *udev)
{ {
...@@ -1052,6 +1048,7 @@ int usb_new_device(struct usb_device *udev) ...@@ -1052,6 +1048,7 @@ int usb_new_device(struct usb_device *udev)
if (err) { if (err) {
dev_err(&udev->dev, "can't set config #%d, error %d\n", dev_err(&udev->dev, "can't set config #%d, error %d\n",
c, err); c, err);
usb_remove_sysfs_dev_files(udev);
device_del(&udev->dev); device_del(&udev->dev);
goto fail; goto fail;
} }
...@@ -1548,8 +1545,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port, ...@@ -1548,8 +1545,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port,
udev->speed = USB_SPEED_LOW; udev->speed = USB_SPEED_LOW;
else else
udev->speed = USB_SPEED_UNKNOWN; udev->speed = USB_SPEED_UNKNOWN;
down (&udev->serialize);
/* set the address */ /* set the address */
choose_address(udev); choose_address(udev);
...@@ -1610,9 +1605,19 @@ static void hub_port_connect_change(struct usb_hub *hub, int port, ...@@ -1610,9 +1605,19 @@ static void hub_port_connect_change(struct usb_hub *hub, int port,
&& highspeed_hubs != 0) && highspeed_hubs != 0)
check_highspeed (hub, udev, port); check_highspeed (hub, udev, port);
/* Run it through the hoops (find a driver, etc) */ /* Store the parent's children[] pointer. At this point
* udev becomes globally accessible, although presumably
* no one will look at it until hdev is unlocked.
*/
down (&udev->serialize);
hdev->children[port] = udev; hdev->children[port] = udev;
/* Run it through the hoops (find a driver, etc) */
status = usb_new_device(udev); status = usb_new_device(udev);
if (status)
hdev->children[port] = NULL;
up (&udev->serialize);
if (status) if (status)
goto loop; goto loop;
...@@ -1622,16 +1627,13 @@ static void hub_port_connect_change(struct usb_hub *hub, int port, ...@@ -1622,16 +1627,13 @@ static void hub_port_connect_change(struct usb_hub *hub, int port,
"%dmA power budget left\n", "%dmA power budget left\n",
2 * status); 2 * status);
up (&udev->serialize);
return; return;
loop: loop:
hdev->children[port] = NULL;
hub_port_disable(hdev, port); hub_port_disable(hdev, port);
usb_disable_endpoint(udev, 0 + USB_DIR_IN); usb_disable_endpoint(udev, 0 + USB_DIR_IN);
usb_disable_endpoint(udev, 0 + USB_DIR_OUT); usb_disable_endpoint(udev, 0 + USB_DIR_OUT);
release_address(udev); release_address(udev);
up (&udev->serialize);
usb_put_dev(udev); usb_put_dev(udev);
if (status == -ENOTCONN) if (status == -ENOTCONN)
break; break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment