# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.596 -> 1.597 # drivers/usb/core/hcd.h 1.4 -> 1.5 # include/linux/usb.h 1.25 -> 1.26 # drivers/usb/core/hub.c 1.21 -> 1.22 # drivers/usb/core/usb.c 1.39 -> 1.40 # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/04/08 david-b@pacbell.net 1.597 # This patch is a more complete fix for the device refcount # sanity checking and cleanup on device disconnect. # # - Splits apart usb_dec_dev_use(), for driver use, and # usb_free_dev(), for hub/hcd use. Both now have # kerneldoc, and will BUG() if the refcount and the # device tree get out of sync. (Except for cleanup of # root hub init errors, refcount must go to zero only # at the instant disconnect processing completes.) # # - More usbcore-internal function declarations are # now moved out of into hcd.h # # - Driver-accessible refcounting is now inlined; minor # code shrinkage, it's using atomic inc/dec instructions # not function calls. # # # -------------------------------------------- # diff -Nru a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h --- a/drivers/usb/core/hcd.h Mon Apr 8 15:49:08 2002 +++ b/drivers/usb/core/hcd.h Mon Apr 8 15:49:08 2002 @@ -181,6 +181,24 @@ #endif /* CONFIG_PCI */ +/* -------------------------------------------------------------------------- */ + +/* Enumeration is only for the hub driver, or HCD virtual root hubs */ +extern struct usb_device *usb_alloc_dev(struct usb_device *parent, + struct usb_bus *); +extern void usb_free_dev(struct usb_device *); +extern int usb_new_device(struct usb_device *dev); +extern void usb_connect(struct usb_device *dev); +extern void usb_disconnect(struct usb_device **); + +#ifndef _LINUX_HUB_H +/* exported to hub driver ONLY to support usb_reset_device () */ +extern int usb_get_configuration(struct usb_device *dev); +extern void usb_set_maxpacket(struct usb_device *dev); +extern void usb_destroy_configuration(struct usb_device *dev); +extern int usb_set_address(struct usb_device *dev); +#endif /* _LINUX_HUB_H */ + /*-------------------------------------------------------------------------*/ /* diff -Nru a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c --- a/drivers/usb/core/hub.c Mon Apr 8 15:49:08 2002 +++ b/drivers/usb/core/hub.c Mon Apr 8 15:49:08 2002 @@ -29,6 +29,7 @@ #include #include +#include "hcd.h" #include "hub.h" /* Wakes up khubd */ diff -Nru a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c --- a/drivers/usb/core/usb.c Mon Apr 8 15:49:08 2002 +++ b/drivers/usb/core/usb.c Mon Apr 8 15:49:08 2002 @@ -826,49 +826,31 @@ return dev; } -// usbcore-internal ... -// but usb_dec_dev_use() is #defined to this, and that's public!! -// FIXME the public call should BUG() whenever count goes to zero, -// the usbcore-internal one should do so _unless_ it does so... +/** + * usb_free_dev - free a usb device structure (usbcore-internal) + * @dev: device that's been disconnected + * Context: !in_interrupt () + * + * Used by hub and virtual root hub drivers. The device is completely + * gone, everything is cleaned up, so it's time to get rid of these last + * records of this device. + */ void usb_free_dev(struct usb_device *dev) { - if (atomic_dec_and_test(&dev->refcnt)) { - /* Normally only goes to zero in usb_disconnect(), from - * khubd or from roothub shutdown (rmmod/apmd/... thread). - * Abnormally, roothub init errors can happen, so HCDs - * call this directly. - * - * Otherwise this is a nasty device driver bug, often in - * disconnect processing. + if (in_interrupt ()) + BUG (); + if (!atomic_dec_and_test (&dev->refcnt)) { + /* MUST go to zero here, else someone's hanging on to + * a device that's supposed to have been cleaned up!! */ - if (in_interrupt ()) - BUG (); - - dev->bus->op->deallocate(dev); - usb_destroy_configuration(dev); - - usb_bus_put(dev->bus); - - kfree(dev); + BUG (); } -} -/** - * usb_inc_dev_use - record another reference to a device - * @dev: the device being referenced - * - * Each live reference to a device should be refcounted. - * - * Device drivers should normally record such references in their - * open() methods. - * Drivers should then release them, using usb_dec_dev_use(), in their - * close() methods. - */ -void usb_inc_dev_use(struct usb_device *dev) -{ - atomic_inc(&dev->refcnt); + dev->bus->op->deallocate (dev); + usb_destroy_configuration (dev); + usb_bus_put (dev->bus); + kfree (dev); } - /** * usb_alloc_urb - creates a new urb for a USB driver to use diff -Nru a/include/linux/usb.h b/include/linux/usb.h --- a/include/linux/usb.h Mon Apr 8 15:49:08 2002 +++ b/include/linux/usb.h Mon Apr 8 15:49:08 2002 @@ -964,24 +964,6 @@ /* -------------------------------------------------------------------------- */ -/* Enumeration is only for the hub driver, or HCD virtual root hubs */ -extern struct usb_device *usb_alloc_dev(struct usb_device *parent, - struct usb_bus *); -extern void usb_free_dev(struct usb_device *); -extern int usb_new_device(struct usb_device *dev); -extern void usb_connect(struct usb_device *dev); -extern void usb_disconnect(struct usb_device **); - -#ifndef _LINUX_HUB_H -/* exported to hub driver ONLY to support usb_reset_device () */ -extern int usb_get_configuration(struct usb_device *dev); -extern void usb_set_maxpacket(struct usb_device *dev); -extern void usb_destroy_configuration(struct usb_device *dev); -extern int usb_set_address(struct usb_device *dev); -#endif /* _LINUX_HUB_H */ - -/* -------------------------------------------------------------------------- */ - /* This is arbitrary. * From USB 2.0 spec Table 11-13, offset 7, a hub can * have up to 255 ports. The most yet reported is 10. @@ -1050,9 +1032,49 @@ /* for drivers using iso endpoints */ extern int usb_get_current_frame_number (struct usb_device *usb_dev); -/* drivers must track when they bind to a device's interfaces */ -extern void usb_inc_dev_use(struct usb_device *); -#define usb_dec_dev_use usb_free_dev +/** + * usb_inc_dev_use - record another reference to a device + * @dev: the device being referenced + * + * Each live reference to a device should be refcounted. + * + * Drivers for USB interfaces should normally record such references in + * their probe() methods, when they bind to an interface, and release + * them usb_dec_dev_use(), in their disconnect() methods. + */ +static inline void usb_inc_dev_use (struct usb_device *dev) +{ + atomic_inc (&dev->refcnt); +} + +/** + * usb_dec_dev_use - drop a reference to a device + * @dev: the device no longer being referenced + * + * Each live reference to a device should be refcounted. + * + * Drivers for USB interfaces should normally release such references in + * their disconnect() methods, and record them in probe(). + * + * Note that driver disconnect() methods must guarantee that when they + * return, all of their outstanding references to the device (and its + * interfaces) are cleaned up. That means that all pending URBs from + * this driver must have completed, and that no more copies of the device + * handle are saved in driver records (including other kernel threads). + */ +static inline void usb_dec_dev_use (struct usb_device *dev) +{ + if (atomic_dec_and_test (&dev->refcnt)) { + /* May only go to zero when usbcore finishes + * usb_disconnect() processing: khubd or HCDs. + * + * If you hit this BUG() it's likely a problem + * with some driver's disconnect() routine. + */ + BUG (); + } +} + /* used these for multi-interface device registration */ extern int usb_find_interface_driver_for_ifnum(struct usb_device *dev, unsigned int ifnum);