Commit c7a01052 authored by Patrick Mochel's avatar Patrick Mochel

driver model: and present field to struct device and implement device_unregister().

device_unregister() is intended to be the complement of device_register(), and assumes
most of the functionality of the current put_device(). It should be called by the bus 
driver when a device is physically removed from the system. 

It should _not_ be called from a driver's remove() method, as that remove() method is called
via device_detach() in device_unregister(). 

dev->present is used to flag the physical presence of the device. It is set when the device
is registered, and cleared when unregistered. get_device() checks this flag, and returns
a NULL pointer if cleared. This prevents anyone from obtaining a reference to a device
that has been unregistered (removed), but not yet been freed (e.g. if someone else is 
holding a reference to it).

put_device() BUG()s if dev->present is set. A device should be unregistered before it is 
freed. This will catch people doing it in the wrong order. 
parent 0bf36f29
......@@ -175,7 +175,7 @@ int device_register(struct device *dev)
INIT_LIST_HEAD(&dev->intf_list);
spin_lock_init(&dev->lock);
atomic_set(&dev->refcount,2);
dev->present = 1;
spin_lock(&device_lock);
if (dev->parent) {
get_device_locked(dev->parent);
......@@ -219,7 +219,7 @@ int device_register(struct device *dev)
struct device * get_device_locked(struct device * dev)
{
struct device * ret = dev;
if (dev && atomic_read(&dev->refcount) > 0)
if (dev && dev->present && atomic_read(&dev->refcount) > 0)
atomic_inc(&dev->refcount);
else
ret = NULL;
......@@ -241,8 +241,35 @@ struct device * get_device(struct device * dev)
*/
void put_device(struct device * dev)
{
struct device * parent;
if (!atomic_dec_and_lock(&dev->refcount,&device_lock))
return;
parent = dev->parent;
dev->parent = NULL;
spin_unlock(&device_lock);
BUG_ON(dev->present);
if (dev->release)
dev->release(dev);
if (parent)
put_device(parent);
}
/**
* device_unregister - unlink device
* @dev: device going away
*
* The device has been removed from the system, so we disavow knowledge
* of it. It might not be the final reference to the device, so we mark
* it as !present, so no more references to it can be acquired.
* In the end, we decrement the final reference count for it.
*/
void device_unregister(struct device * dev)
{
spin_lock(&device_lock);
dev->present = 0;
list_del_init(&dev->node);
list_del_init(&dev->g_list);
list_del_init(&dev->bus_list);
......@@ -267,11 +294,7 @@ void put_device(struct device * dev)
/* remove the driverfs directory */
device_remove_dir(dev);
if (dev->release)
dev->release(dev);
if (dev->parent)
put_device(dev->parent);
put_device(dev);
}
static int __init device_init(void)
......@@ -287,5 +310,6 @@ static int __init device_init(void)
core_initcall(device_init);
EXPORT_SYMBOL(device_register);
EXPORT_SYMBOL(device_unregister);
EXPORT_SYMBOL(get_device);
EXPORT_SYMBOL(put_device);
......@@ -289,6 +289,7 @@ struct device {
void *platform_data; /* Platform specific data (e.g. ACPI,
BIOS data relevant to device) */
u32 present;
u32 current_state; /* Current operating state. In
ACPI-speak, this is D0-D3, D0
being fully functional, and D3
......@@ -327,7 +328,7 @@ dev_set_drvdata (struct device *dev, void *data)
* High level routines for use by the bus drivers
*/
extern int device_register(struct device * dev);
extern void device_unregister(struct device * dev);
/* driverfs interface for exporting device attributes */
......
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