Commit 99e471c7 authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

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 <linux/usb.h> into hcd.h
  
    - Driver-accessible refcounting is now inlined; minor
      code shrinkage, it's using atomic inc/dec instructions
      not function calls.

<note from greg k-h, there is still some work to be done with USB device
 reference counting, but this patch is a step in the right direction.>
parent 3066fa91
...@@ -181,6 +181,24 @@ extern int usb_hcd_pci_resume (struct pci_dev *dev); ...@@ -181,6 +181,24 @@ extern int usb_hcd_pci_resume (struct pci_dev *dev);
#endif /* CONFIG_PCI */ #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 */
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
/* /*
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include "hcd.h"
#include "hub.h" #include "hub.h"
/* Wakes up khubd */ /* Wakes up khubd */
......
...@@ -826,50 +826,32 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus) ...@@ -826,50 +826,32 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus)
return dev; return dev;
} }
// usbcore-internal ... /**
// but usb_dec_dev_use() is #defined to this, and that's public!! * usb_free_dev - free a usb device structure (usbcore-internal)
// FIXME the public call should BUG() whenever count goes to zero, * @dev: device that's been disconnected
// the usbcore-internal one should do so _unless_ it does so... * 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) 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 ()) if (in_interrupt ())
BUG (); BUG ();
if (!atomic_dec_and_test (&dev->refcnt)) {
dev->bus->op->deallocate(dev); /* MUST go to zero here, else someone's hanging on to
usb_destroy_configuration(dev); * a device that's supposed to have been cleaned up!!
*/
usb_bus_put(dev->bus); BUG ();
kfree(dev);
} }
}
/** dev->bus->op->deallocate (dev);
* usb_inc_dev_use - record another reference to a device usb_destroy_configuration (dev);
* @dev: the device being referenced usb_bus_put (dev->bus);
* kfree (dev);
* 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);
} }
/** /**
* usb_alloc_urb - creates a new urb for a USB driver to use * usb_alloc_urb - creates a new urb for a USB driver to use
* @iso_packets: number of iso packets for this urb * @iso_packets: number of iso packets for this urb
......
...@@ -962,24 +962,6 @@ struct usb_tt { ...@@ -962,24 +962,6 @@ struct usb_tt {
}; };
/* -------------------------------------------------------------------------- */
/* 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. /* This is arbitrary.
...@@ -1050,9 +1032,49 @@ extern int usb_reset_device(struct usb_device *dev); ...@@ -1050,9 +1032,49 @@ extern int usb_reset_device(struct usb_device *dev);
/* for drivers using iso endpoints */ /* for drivers using iso endpoints */
extern int usb_get_current_frame_number (struct usb_device *usb_dev); 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 *); * usb_inc_dev_use - record another reference to a device
#define usb_dec_dev_use usb_free_dev * @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 */ /* used these for multi-interface device registration */
extern int usb_find_interface_driver_for_ifnum(struct usb_device *dev, unsigned int ifnum); extern int usb_find_interface_driver_for_ifnum(struct usb_device *dev, unsigned int ifnum);
......
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